diff --git a/.circleci/config.yml b/.circleci/config.yml index 1a462b680..e2905a425 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,29 +1,47 @@ +run_with_build_env_vars: &run_with_build_env_vars + environment: + LLVM_SYS_70_PREFIX: /home/circleci/project/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/ + +run_install_dependencies: &run_install_dependencies + run: + 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 + version: 2 jobs: + changelog: + docker: + - image: docker:stable-git + steps: + - checkout + - run: + command: ! git diff --exit-code CHANGELOG.md + # Job used for testing lint: docker: - image: circleci/rust:latest + <<: *run_with_build_env_vars steps: - checkout - restore_cache: keys: - - v6-lint-{{ arch }}-{{ checksum "Cargo.lock" }} - - run: - 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 + - v8-lint-{{ arch }}-{{ checksum "Cargo.lock" }} + - v8-lint-{{ arch }} + - <<: *run_install_dependencies - run: name: Install lint deps command: | + git config --global --unset url."ssh://git@github.com".insteadOf || true + rustup toolchain install nightly-2019-02-27 rustup component add rustfmt - rustup component add clippy + rustup component add clippy --toolchain=nightly-2019-02-27 || cargo +nightly-2019-02-27 install --git https://github.com/rust-lang/rust-clippy/ --force clippy - run: name: Execute lints command: | - export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/" make lint - save_cache: paths: @@ -31,44 +49,37 @@ jobs: - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: v6-lint-{{ arch }}-{{ checksum "Cargo.lock" }} + key: v8-lint-{{ arch }}-{{ checksum "Cargo.lock" }} test: docker: - image: circleci/rust:latest + <<: *run_with_build_env_vars steps: - checkout - restore_cache: keys: - - v6-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }} - - run: - 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 + - v8-test-cargo-cache-linux-stable-{{ arch }}-{{ checksum "Cargo.lock" }} + - v8-test-cargo-cache-linux-stable-{{ arch }} + - <<: *run_install_dependencies - run: name: Tests - command: | - export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/" - make test + command: 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 + make test-emscripten-clif + make test-emscripten-llvm - 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 + command: make integration-tests - save_cache: paths: - /usr/local/cargo/registry - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: v6-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }} + key: v8-test-cargo-cache-linux-stable-{{ arch }}-{{ checksum "Cargo.lock" }} test-macos: macos: @@ -77,7 +88,8 @@ jobs: - checkout - restore_cache: keys: - - v6-cargo-cache-darwin-{{ arch }}-{{ checksum "Cargo.lock" }} + - v8-cargo-cache-darwin-stable-{{ arch }}-{{ checksum "Cargo.lock" }} + - v8-cargo-cache-darwin-stable-{{ arch }} - run: name: Install crate dependencies command: | @@ -91,7 +103,7 @@ jobs: - run: name: Install Rust command: | - curl https://sh.rustup.rs -sSf | sh -s -- -y + curl -sSf https://sh.rustup.rs | sh -s -- -y export PATH="$HOME/.cargo/bin:$PATH" cargo --version - run: @@ -113,7 +125,8 @@ jobs: # 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 + make test-emscripten-clif + make test-emscripten-llvm - run: name: Integration Tests command: | @@ -130,22 +143,30 @@ jobs: - target/release/.fingerprint - target/release/build - target/release/deps - key: v6-cargo-cache-darwin-{{ arch }}-{{ checksum "Cargo.lock" }} + key: v8-cargo-cache-darwin-stable-{{ arch }}-{{ checksum "Cargo.lock" }} test-and-build: docker: - image: circleci/rust:latest steps: - checkout + - run: + name: "Pull dependencies" + command: | + git submodule init + git submodule update --remote - restore_cache: keys: - - v6-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }} - - run: + - v8-cargo-cache-linux-nightly-{{ arch }}-{{ checksum "Cargo.lock" }} + - v8-cargo-cache-linux-nightly-{{ arch }} + - run: 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 + # Use rust nightly (for singlepass, for now) + - run: rustup default nightly-2019-04-11 - run: name: Tests command: | @@ -155,18 +176,25 @@ jobs: 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 + make test-emscripten-clif + make test-emscripten-llvm - 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 + make production-release + cargo build --release --manifest-path wapm-cli/Cargo.toml --features telemetry mkdir -p artifacts VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2) # GIT_VERSION=$(git describe --exact-match --tags) echo "${VERSION}" >> artifacts/version echo "${CIRCLE_TAG}" >> artifacts/git_version - cp ./target/release/wasmer ./artifacts/$(./binary-name.sh) + make build-install + cp ./wasmer.tar.gz ./artifacts/$(./binary-name.sh) + - run: + name: Debug flag checked + command: | + cargo check --features "debug" - persist_to_workspace: root: . paths: @@ -180,16 +208,25 @@ jobs: - target/release/.fingerprint - target/release/build - target/release/deps - key: v6-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }} + - wapm-cli/target/release/.fingerprint + - wapm-cli/target/release/build + - wapm-cli/target/release/deps + key: v8-cargo-cache-linux-nightly-{{ arch }}-{{ checksum "Cargo.lock" }} test-and-build-macos: macos: xcode: "9.0" steps: - checkout + - run: + name: "Pull dependencies" + command: | + git submodule init + git submodule update --remote - restore_cache: keys: - - v6-cargo-cache-darwin-{{ arch }}-{{ checksum "Cargo.lock" }} + - v8-cargo-cache-darwin-nightly-{{ arch }}-{{ checksum "Cargo.lock" }} + - v8-cargo-cache-darwin-nightly-{{ arch }} - run: name: Install crate dependencies command: | @@ -203,9 +240,15 @@ jobs: - run: name: Install Rust command: | - curl https://sh.rustup.rs -sSf | sh -s -- -y + curl -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain nightly export PATH="$HOME/.cargo/bin:$PATH" cargo --version + # Use rust nightly (for singlepass, for now) + # - run: + # name: Install Rust nightly + # command: | + # export PATH="$HOME/.rustup/bin:$PATH" + # rustup default nightly-2019-04-11 - run: name: Tests command: | @@ -225,18 +268,21 @@ jobs: # 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 + make test-emscripten-clif + make test-emscripten-singlepass - 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 + make production-release + cargo build --release --manifest-path wapm-cli/Cargo.toml --features telemetry mkdir -p artifacts + make build-install + cp ./wasmer.tar.gz ./artifacts/$(./binary-name.sh) # VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2) # echo "${VERSION}" >> artifacts/version - cp ./target/release/wasmer ./artifacts/$(./binary-name.sh) - persist_to_workspace: root: . paths: @@ -250,7 +296,10 @@ jobs: - target/release/.fingerprint - target/release/build - target/release/deps - key: v6-cargo-cache-darwin-{{ arch }}-{{ checksum "Cargo.lock" }} + - wapm-cli/target/release/.fingerprint + - wapm-cli/target/release/build + - wapm-cli/target/release/deps + key: v8-cargo-cache-darwin-nightly-{ arch }}-{{ checksum "Cargo.lock" }} test-rust-nightly: docker: @@ -259,24 +308,28 @@ jobs: - checkout - restore_cache: keys: - - v6-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}-nightly - - run: + - v8-cargo-cache-linux-nightly-{{ arch }}-{{ checksum "Cargo.lock" }} + - v8-cargo-cache-linux-nightly-{{ arch }} + - run: 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: rustup default nightly-2019-04-11 - run: | export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/" make test + make test-singlepass + make test-emscripten-clif + make test-emscripten-singlepass - save_cache: paths: - /usr/local/cargo/registry - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: v6-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}-nightly + key: v8-cargo-cache-linux-nightly-{{ arch }}-{{ checksum "Cargo.lock" }}-nightly publish-github-release: docker: @@ -303,11 +356,28 @@ jobs: # echo "Versions don't match. Wasmer output version (wasmer --version) is ${VERSION} while Git tag is ${VERSION_TAG}" # exit 1 #fi + trigger-benchmark-build: + docker: + - image: circleci/rust:latest + steps: + - run: + name: "Trigger Benchmark Build" + command: | + if [[ -z "${CIRCLE_API_USER_TOKEN}" ]]; then + echo "CIRCLE_API_USER_TOKEN environment variable not set" + exit 1 + else + echo "Triggering benchmark build" + curl -u ${CIRCLE_API_USER_TOKEN} \ + -d build_parameters[CIRCLE_JOB]=bench \ + https://circleci.com/api/v1.1/project/github/wasmerio/wasmer-bench/tree/master + fi workflows: version: 2 main: jobs: + - changelog - lint - test: filters: @@ -345,3 +415,10 @@ workflows: filters: branches: only: master + - trigger-benchmark-build: + requires: + - test-and-build + - lint + filters: + branches: + only: master diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..e861ea7b6 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +# Ignore everything +** +!lib/** +!src/** +!Cargo.toml +!Cargo.lock \ No newline at end of file diff --git a/.gitignore b/.gitignore index 141efbf78..d1122a798 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ .DS_Store .idea **/.vscode +install/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..af96cdf0e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "wapm-cli"] + path = wapm-cli + url = https://github.com/wasmerio/wapm-cli.git diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..92b3c8221 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,53 @@ +# Changelog + +All PRs to the Wasmer repository must add to this file. + +Blocks of changes will separated by version increments. + +## **[Unreleased]** + +- [#432](https://github.com/wasmerio/wasmer/pull/432) Fix returned value of `wasmer_last_error_message` in the runtime C API +- [#429](https://github.com/wasmerio/wasmer/pull/429) Get wasi::path_filestat_get working for some programs; misc. minor WASI FS improvements +- [#413](https://github.com/wasmerio/wasmer/pull/413) Update LLVM backend to use new parser codegen traits + +## 0.4.1 - 2018-05-06 + +- [#426](https://github.com/wasmerio/wasmer/pull/426) Update wapm-cli submodule, bump version to 0.4.1 +- [#422](https://github.com/wasmerio/wasmer/pull/422) Improved Emscripten functions to run optipng and pngquant compiled to wasm +- [#409](https://github.com/wasmerio/wasmer/pull/409) Improved Emscripten functions to run JavascriptCore compiled to wasm +- [#399](https://github.com/wasmerio/wasmer/pull/399) Add example of using a plugin extended from WASI +- [#397](https://github.com/wasmerio/wasmer/pull/397) Fix WASI fs abstraction to work on Windows +- [#390](https://github.com/wasmerio/wasmer/pull/390) Pin released wapm version and add it as a git submodule +- [#408](https://github.com/wasmerio/wasmer/pull/408) Add images to windows installer and update installer to add wapm bin directory to path + +## 0.4.0 - 2018-04-23 + +- [#383](https://github.com/wasmerio/wasmer/pull/383) Hook up wasi exit code to wasmer cli. +- [#382](https://github.com/wasmerio/wasmer/pull/382) Improve error message on `--backend` flag to only suggest currently enabled backends +- [#381](https://github.com/wasmerio/wasmer/pull/381) Allow retrieving propagated user errors. +- [#379](https://github.com/wasmerio/wasmer/pull/379) Fix small return types from imported functions. +- [#371](https://github.com/wasmerio/wasmer/pull/371) Add more Debug impl for WASI types +- [#368](https://github.com/wasmerio/wasmer/pull/368) Fix issue with write buffering +- [#343](https://github.com/wasmerio/wasmer/pull/343) Implement preopened files for WASI and fix aligment issue when accessing WASI memory +- [#367](https://github.com/wasmerio/wasmer/pull/367) Add caching support to the LLVM backend. +- [#366](https://github.com/wasmerio/wasmer/pull/366) Remove `UserTrapper` trait to fix [#365](https://github.com/wasmerio/wasmer/issues/365). +- [#348](https://github.com/wasmerio/wasmer/pull/348) Refactor internal runtime ↔️ backend abstraction. +- [#355](https://github.com/wasmerio/wasmer/pull/355) Misc changes to `Cargo.toml`s for publishing +- [#352](https://github.com/wasmerio/wasmer/pull/352) Bump version numbers to 0.3.0 +- [#351](https://github.com/wasmerio/wasmer/pull/351) Add hidden option to specify wasm program name (can be used to improve error messages) +- [#350](https://github.com/wasmerio/wasmer/pull/350) Enforce that CHANGELOG.md is updated through CI. +- [#349](https://github.com/wasmerio/wasmer/pull/349) Add [CHANGELOG.md](https://github.com/wasmerio/wasmer/blob/master/CHANGELOG.md). + +## 0.3.0 - 2018-04-12 + +- [#276](https://github.com/wasmerio/wasmer/pull/276) [#288](https://github.com/wasmerio/wasmer/pull/288) [#344](https://github.com/wasmerio/wasmer/pull/344) Use new singlepass backend (with the `--backend=singlepass` when running Wasmer) +- [#338](https://github.com/wasmerio/wasmer/pull/338) Actually catch traps/panics/etc when using a typed func. +- [#325](https://github.com/wasmerio/wasmer/pull/325) Fixed func_index in debug mode +- [#323](https://github.com/wasmerio/wasmer/pull/323) Add validate subcommand to validate Wasm files +- [#321](https://github.com/wasmerio/wasmer/pull/321) Upgrade to Cranelift 0.3.0 +- [#319](https://github.com/wasmerio/wasmer/pull/319) Add Export and GlobalDescriptor to Runtime API +- [#310](https://github.com/wasmerio/wasmer/pull/310) Cleanup warnings +- [#299](https://github.com/wasmerio/wasmer/pull/299) [#300](https://github.com/wasmerio/wasmer/pull/300) [#301](https://github.com/wasmerio/wasmer/pull/301) [#303](https://github.com/wasmerio/wasmer/pull/303) [#304](https://github.com/wasmerio/wasmer/pull/304) [#305](https://github.com/wasmerio/wasmer/pull/305) [#306](https://github.com/wasmerio/wasmer/pull/306) [#307](https://github.com/wasmerio/wasmer/pull/307) Add support for WASI 🎉 +- [#286](https://github.com/wasmerio/wasmer/pull/286) Add extend to imports +- [#278](https://github.com/wasmerio/wasmer/pull/278) Add versioning to cache +- [#250](https://github.com/wasmerio/wasmer/pull/250) Setup bors diff --git a/Cargo.lock b/Cargo.lock index a6d14b571..4a0e4d5d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,17 +1,47 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "MacTypes-sys" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "adler32" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "aho-corasick" -version = "0.6.10" +version = "0.7.3" 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 = "android_log-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "android_logger" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "android_log-sys 0.1.2 (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)", +] + [[package]] name = "ansi_term" version = "0.11.0" 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 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -32,9 +62,9 @@ name = "atty" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (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)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -44,15 +74,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "backtrace" -version = "0.3.14" +version = "0.3.15" 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)", + "libc 0.2.51 (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)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -60,8 +90,16 @@ 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)", + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "base64" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -70,7 +108,7 @@ 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)", + "cexpr 0.3.5 (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)", @@ -81,7 +119,7 @@ dependencies = [ "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)", + "regex 1.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "which 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -101,11 +139,52 @@ dependencies = [ "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "blob" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "build_const" +version = "0.2.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 = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bzip2" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bzip2-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "capstone" version = "0.5.0" @@ -119,7 +198,7 @@ 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)", + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -129,32 +208,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cbindgen" -version = "0.8.2" +version = "0.8.3" 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 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.90 (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)", + "syn 0.15.30 (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.30" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "cexpr" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "nom 4.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -168,7 +250,7 @@ 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)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -196,10 +278,10 @@ dependencies = [ [[package]] name = "cmake" -version = "0.1.35" +version = "0.1.38" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -208,73 +290,106 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "cranelift-bforest" -version = "0.26.0" +name = "core-foundation" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cranelift-entity 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "core-foundation-sys" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cranelift-bforest" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cranelift-entity 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cranelift-codegen" -version = "0.26.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cranelift-bforest 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-codegen-meta 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-entity 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-bforest 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen-meta 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "target-lexicon 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cranelift-codegen-meta" -version = "0.26.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cranelift-entity 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cranelift-entity" -version = "0.26.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cranelift-frontend" -version = "0.26.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cranelift-codegen 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "target-lexicon 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cranelift-native" -version = "0.26.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cranelift-codegen 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", "raw-cpuid 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "target-lexicon 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cranelift-wasm" -version = "0.26.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-codegen 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-entity 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-frontend 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-frontend 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmparser 0.22.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crc" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crc32fast" +version = "1.2.0" +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]] @@ -286,7 +401,7 @@ dependencies = [ "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)", + "csv 1.0.6 (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)", @@ -294,8 +409,8 @@ dependencies = [ "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 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.90 (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)", @@ -320,6 +435,15 @@ dependencies = [ "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crossbeam-deque" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crossbeam-epoch" version = "0.3.1" @@ -334,6 +458,27 @@ dependencies = [ "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crossbeam-epoch" +version = "0.7.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.6.5 (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)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-queue" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crossbeam-utils" version = "0.2.2" @@ -342,13 +487,24 @@ dependencies = [ "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crossbeam-utils" +version = "0.6.5" +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)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "csv" -version = "1.0.5" +version = "1.0.6" 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)", + "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.90 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -368,10 +524,47 @@ dependencies = [ ] [[package]] -name = "either" -version = "1.5.1" +name = "dtoa" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "dynasm" +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.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.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.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dynasmrt" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "either" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "encoding_rs" +version = "0.8.17" +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 = "enum-methods" version = "0.0.8" @@ -389,7 +582,7 @@ 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)", + "regex 1.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -399,8 +592,8 @@ 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.50 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -409,7 +602,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.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -417,7 +610,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)", + "backtrace 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -428,7 +621,7 @@ 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)", + "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -437,16 +630,90 @@ name = "field-offset" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "filetime" +version = "0.2.4" +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.51 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.52 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "flate2" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide_c_api 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fnv" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "fuchsia-cprng" 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" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-cpupool" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.26 (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 = "gcc" version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "generational-arena" +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 = "generic-array" version = "0.12.0" @@ -470,6 +737,23 @@ dependencies = [ "scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "h2" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "hashbrown" version = "0.1.8" @@ -477,7 +761,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.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -493,6 +777,21 @@ name = "hex" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "http" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "httparse" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "humantime" version = "1.2.0" @@ -501,6 +800,55 @@ dependencies = [ "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "hyper" +version = "0.12.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "h2 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hyper-tls" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.25 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "idna" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "indexmap" version = "1.0.2" @@ -509,22 +857,31 @@ 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" +source = "git+https://github.com/wasmerio/inkwell?branch=llvm7-0#a14e62977504ef574dc2e933edc559cc79781ca7" dependencies = [ - "either 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.2 (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)", + "libc 0.2.51 (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" +source = "git+https://github.com/wasmerio/inkwell?branch=llvm7-0#a14e62977504ef574dc2e933edc559cc79781ca7" 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)", + "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "iovec" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -532,7 +889,7 @@ 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)", + "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -555,17 +912,41 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "libc" -version = "0.2.50" +name = "lazycell" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "libc" +version = "0.2.51" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libflate" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] 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)", + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_test 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -573,10 +954,10 @@ 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)", + "cc 1.0.34 (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)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -597,12 +978,44 @@ dependencies = [ "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "lz4" +version = "1.23.1" +source = "git+https://github.com/zboxfs/lz4-rs.git#4704144553d827a96d4fedeb683dbde1360e06e3" +dependencies = [ + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "lz4-sys 1.8.3 (git+https://github.com/zboxfs/lz4-rs.git)", +] + +[[package]] +name = "lz4-sys" +version = "1.8.3" +source = "git+https://github.com/zboxfs/lz4-rs.git#4704144553d827a96d4fedeb683dbde1360e06e3" +dependencies = [ + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "matches" +version = "0.1.8" +source = "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)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "memmap" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -610,8 +1023,8 @@ name = "memmap" version = "0.7.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)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -619,15 +1032,109 @@ name = "memoffset" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "mime" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicase 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mime_guess" +version = "2.0.0-alpha.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", + "phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miniz_oxide" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miniz_oxide_c_api" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", + "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio" +version = "0.6.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miow" +version = "0.2.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)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "native-tls" +version = "0.2.2" +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)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.20 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.43 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "net2" +version = "0.2.33" +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.51 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (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.30 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.34 (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)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -637,9 +1144,9 @@ 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.30 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.34 (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)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -650,13 +1157,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "nom" -version = "4.2.2" +version = "4.2.3" 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.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num-traits" version = "0.2.6" @@ -667,7 +1182,45 @@ 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)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl" +version = "0.10.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (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.51 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.43 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl-probe" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "openssl-sys" +version = "0.9.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "owning_ref" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -684,7 +1237,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.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -702,11 +1255,11 @@ name = "parking_lot_core" version = "0.4.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)", + "libc 0.2.51 (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.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -714,11 +1267,65 @@ name = "peeking_take_while" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "percent-encoding" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "phf" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_codegen" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_generator" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_shared" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pkg-config" +version = "0.3.14" +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 = "plugin-for-example" +version = "0.1.0" + +[[package]] +name = "podio" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "proc-macro2" version = "0.4.27" @@ -745,13 +1352,25 @@ dependencies = [ "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (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.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand" 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.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (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)", @@ -760,7 +1379,7 @@ dependencies = [ "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)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -806,9 +1425,9 @@ name = "rand_jitter" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (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)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -818,10 +1437,10 @@ 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-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)", + "libc 0.2.51 (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)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -856,7 +1475,7 @@ 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.30 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -866,7 +1485,7 @@ 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)", + "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -877,7 +1496,7 @@ 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)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -891,7 +1510,7 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.1.51" +version = "0.1.52" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -899,24 +1518,24 @@ name = "redox_termios" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.52 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex" -version = "1.1.2" +version = "1.1.5" 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)", + "aho-corasick 0.7.3 (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)", + "regex-syntax 0.6.6 (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" +version = "0.6.6" 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)", @@ -927,7 +1546,55 @@ 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)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "reqwest" +version = "0.9.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding_rs 0.8.17 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.25 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", + "mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rmp" +version = "0.8.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)", + "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rmp-serde" +version = "0.13.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)", + "rmp 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -956,6 +1623,15 @@ dependencies = [ "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "schannel" +version = "0.1.15" +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)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "scopeguard" version = "0.3.3" @@ -977,7 +1653,28 @@ 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)", + "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "security-framework" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "security-framework-sys" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "MacTypes-sys 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -995,10 +1692,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.89" +version = "1.0.90" 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)", + "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1007,7 +1704,7 @@ 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.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1015,17 +1712,17 @@ name = "serde_bytes" version = "0.10.5" 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 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" -version = "1.0.89" +version = "1.0.90" 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)", + "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1035,9 +1732,38 @@ 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.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "serde_test" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_urlencoded" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "siphasher" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "smallvec" version = "0.6.9" @@ -1048,6 +1774,11 @@ name = "stable_deref_trait" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "string" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "strsim" version = "0.7.0" @@ -1070,7 +1801,7 @@ dependencies = [ "heck 0.3.1 (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.29 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1085,7 +1816,7 @@ dependencies = [ [[package]] name = "syn" -version = "0.15.29" +version = "0.15.30" 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)", @@ -1108,13 +1839,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)", - "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.30 (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 = "take_mut" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "tar" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "filetime 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.52 (registry+https://github.com/rust-lang/crates.io-index)", + "xattr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "target-lexicon" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1122,17 +1869,26 @@ dependencies = [ "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tempdir" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.5.1 (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)", + "libc 0.2.51 (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)", + "redox_syscall 0.1.52 (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)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1148,8 +1904,8 @@ name = "termion" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "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)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.52 (registry+https://github.com/rust-lang/crates.io-index)", "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1174,9 +1930,9 @@ name = "time" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "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)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.52 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1184,18 +1940,144 @@ 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 1.0.90 (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 = "tokio" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-trace-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-current-thread" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-executor" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-io" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-reactor" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (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)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-sync 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-sync" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-tcp" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-threadpool" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-timer" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-trace-core" +version = "0.1.0" +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 = "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)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "try-lock" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "typenum" version = "1.10.0" @@ -1206,6 +2088,38 @@ name = "ucd-util" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicase" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicase" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "unicode-segmentation" version = "1.2.1" @@ -1226,11 +2140,34 @@ name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "url" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "utf8-ranges" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "uuid" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "vcpkg" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "vec_map" version = "0.8.1" @@ -1251,8 +2188,8 @@ name = "wabt" version = "0.7.4" 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_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.90 (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)", ] @@ -1262,8 +2199,8 @@ name = "wabt-sys" version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "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)", + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", + "cmake 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1273,112 +2210,153 @@ 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 0.3.7 (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.2.1" +name = "want" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasmer" +version = "0.4.1" +dependencies = [ + "errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (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.2.0", - "wasmer-emscripten 0.2.1", - "wasmer-llvm-backend 0.1.0", - "wasmer-runtime 0.2.1", - "wasmer-runtime-core 0.2.1", + "wasmer-clif-backend 0.4.1", + "wasmer-emscripten 0.4.1", + "wasmer-llvm-backend 0.4.1", + "wasmer-middleware-common 0.4.1", + "wasmer-runtime 0.4.1", + "wasmer-runtime-abi 0.4.1", + "wasmer-runtime-core 0.4.1", + "wasmer-singlepass-backend 0.4.1", + "wasmer-wasi 0.4.1", ] [[package]] name = "wasmer-clif-backend" -version = "0.2.0" +version = "0.4.1" 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)", - "cranelift-entity 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", - "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)", + "cranelift-codegen 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-native 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-wasm 0.30.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.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.13.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 1.0.90 (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.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.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)", + "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "target-lexicon 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime-core 0.4.1", + "wasmer-win-exception-handler 0.4.1", + "wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmer-emscripten" -version = "0.2.1" +version = "0.4.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)", "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)", + "libc 0.2.51 (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.2.0", - "wasmer-llvm-backend 0.1.0", - "wasmer-runtime-core 0.2.1", + "wasmer-clif-backend 0.4.1", + "wasmer-llvm-backend 0.4.1", + "wasmer-runtime-core 0.4.1", + "wasmer-singlepass-backend 0.4.1", ] [[package]] name = "wasmer-llvm-backend" -version = "0.1.0" +version = "0.4.1" 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)", + "cc 1.0.34 (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)", + "libc 0.2.51 (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)", + "regex 1.1.5 (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)", + "wasmer-runtime-core 0.4.1", + "wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasmer-middleware-common" +version = "0.4.1" +dependencies = [ + "wasmer-runtime-core 0.4.1", ] [[package]] name = "wasmer-runtime" -version = "0.2.1" +version = "0.4.1" dependencies = [ "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-llvm-backend 0.1.0", - "wasmer-runtime-core 0.2.1", + "wasmer-clif-backend 0.4.1", + "wasmer-llvm-backend 0.4.1", + "wasmer-runtime-core 0.4.1", + "wasmer-singlepass-backend 0.4.1", +] + +[[package]] +name = "wasmer-runtime-abi" +version = "0.4.1" +dependencies = [ + "failure 0.1.5 (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.51 (registry+https://github.com/rust-lang/crates.io-index)", + "tar 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime-core 0.4.1", + "wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)", + "zbox 0.6.1 (git+https://github.com/wasmerio/zbox?branch=bundle-libsodium)", + "zstd 0.4.22+zstd.1.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmer-runtime-c-api" -version = "0.2.1" +version = "0.4.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", + "cbindgen 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime 0.4.1", + "wasmer-runtime-core 0.4.1", ] [[package]] name = "wasmer-runtime-core" -version = "0.2.1" +version = "0.4.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)", @@ -1388,53 +2366,75 @@ dependencies = [ "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.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (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.89 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (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.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)", + "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasmer-singlepass-backend" +version = "0.4.1" +dependencies = [ + "byteorder 1.3.1 (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)", + "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)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime-core 0.4.1", + "wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmer-spectests" -version = "0.2.0" +version = "0.4.1" dependencies = [ "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.2.0", - "wasmer-llvm-backend 0.1.0", - "wasmer-runtime-core 0.2.1", + "wasmer-clif-backend 0.4.1", + "wasmer-llvm-backend 0.4.1", + "wasmer-runtime-core 0.4.1", + "wasmer-singlepass-backend 0.4.1", +] + +[[package]] +name = "wasmer-wasi" +version = "0.4.1" +dependencies = [ + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "generational-arena 0.2.2 (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.51 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime-core 0.4.1", ] [[package]] name = "wasmer-win-exception-handler" -version = "0.2.0" +version = "0.4.1" 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)", + "cmake 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime-core 0.4.1", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmparser" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "wasmparser" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "wasmparser" -version = "0.28.0" +version = "0.29.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1443,7 +2443,7 @@ 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)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1453,7 +2453,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winapi" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1475,7 +2475,7 @@ 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)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1488,51 +2488,153 @@ 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 0.3.7 (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 = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "xattr" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "zbox" +version = "0.6.1" +source = "git+https://github.com/wasmerio/zbox?branch=bundle-libsodium#113c62bf3f94124c4978959043efcf98222fa626" +dependencies = [ + "android_logger 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libflate 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lz4 1.23.1 (git+https://github.com/zboxfs/lz4-rs.git)", + "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.9.13 (registry+https://github.com/rust-lang/crates.io-index)", + "rmp-serde 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "tar 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "zip 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "zip" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libflate 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "zstd" +version = "0.4.22+zstd.1.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "zstd-safe 1.4.7+zstd.1.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "zstd-safe" +version = "1.4.7+zstd.1.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "zstd-sys 1.4.8+zstd.1.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "zstd-sys" +version = "1.4.8+zstd.1.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "blob 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.34 (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.51 (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 MacTypes-sys 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eaf9f0d0b1cc33a4d2aee14fb4b2eac03462ef4db29c8ac4057327d8a71ad86f" +"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" +"checksum aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e6f484ae0c99fec2e858eb6134949117399f222608d84cadb3f58c1f97c2364c" +"checksum android_log-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b8052e2d8aabbb8d556d6abbcce2a22b9590996c5f849b9c7ce4544a2e3b984e" +"checksum android_logger 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cf44378e81264148f08e58336674542f82d0021f685d0be0320c82e1653dbe0b" "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 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "f106c02a3604afcdc0df5d36cc47b44b55917dbaf3d808f71c163a0ddba64637" "checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" +"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" "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 blake2b_simd 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce2571a6cd634670daa2977cc894c1cc2ba57c563c498e5a82c35446f34d056e" +"checksum blob 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "19803aa44ff8b43123bbe369efaddcb638ea7dc332e543972dd95ac7cb148b92" +"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" +"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +"checksum bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b" +"checksum bzip2-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6584aa36f5ad4c9247f5323b0a42f37802b37a836f0ad87084d7a33961abe25f" "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 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 cbindgen 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "31f70db109be74a3dfcb0af4d0d191e52230351477f14c2ed10707c2b0dcfa2e" +"checksum cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)" = "30f813bf45048a18eda9190fd3c6b78644146056740c43172a5a3699118588fd" +"checksum cexpr 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7fa24eb00d5ffab90eaeaf1092ac85c04c64aaf358ea6f84505b8116d24c6af" "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 cmake 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "96210eec534fc3fbfc0452a63769424eaa80205fda6cea98e5b61cb3d97bcec8" "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" -"checksum cranelift-entity 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4df40e26c0cf7b4d86919cb995bb412ee3001cc18e4f3c83a903f30b7007d8b" -"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 core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "286e0b41c3a20da26536c6000a280585d519fd07b3956b43aed8a79e9edce980" +"checksum core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "716c271e8613ace48344f723b60b900a93150271e5be206212d052bbc0883efa" +"checksum cranelift-bforest 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e5a357d20666bf4a8c2d626a19f1b59dbca66cd844fb1e66c5612254fd0f7505" +"checksum cranelift-codegen 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab00cb149a5bb0f7e6dd391357356a5d71c335a431e8eece94f32da2d5a043f7" +"checksum cranelift-codegen-meta 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e3797a2f450ac71297e083dd440d0cdd0d3bceabe4a3ca6bcb9e4077e9c0327d" +"checksum cranelift-entity 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b66e28877b75b3d2b31250f780bb5db8f68ae3df681cd56add803b2567ac4fd" +"checksum cranelift-frontend 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b72d55fd732b1f7a99d043a36c54a5679b6ec8bc777c8d954fb97c4fa0fce7eb" +"checksum cranelift-native 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0239f34836621a127c2132980b2f5c32a1be1c40e2d1a9a1a9bd5af33c12aee" +"checksum cranelift-wasm 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "740ebfba28c8433f06750f84819f1eb663ea9f5e4b9a81c01f4e52262d868b56" +"checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" +"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" "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-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" +"checksum crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "04c9e3102cc2d69cd681412141b390abd55a362afc1540965dad0ad4d34280b4" +"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" "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 crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" +"checksum csv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f0782c7154d8dd08f4adeb5aa22ab178c10281915f7da68d10bb646f03aaee73" "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 either 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c67353c641dc847124ea1902d69bd753dee9bb3beff9aa3662ecf86c971d1fac" +"checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd" +"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.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" +"checksum encoding_rs 0.8.17 (registry+https://github.com/rust-lang/crates.io-index)" = "4155785c79f2f6701f185eb2e6b4caf0555ec03477cb4c70db67b465311620ed" "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" @@ -1540,46 +2642,91 @@ dependencies = [ "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 field-offset 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "64e9bc339e426139e02601fa69d101e96a92aee71b58bc01697ec2a63a5c9e68" +"checksum filetime 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a2df5c1a8c4be27e7707789dc42ae65976e60b394afd293d1419ab915833e646" +"checksum flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f87e68aa82b2de08a6e037f1385455759df6e445a8df5e005b4297191dbf18aa" +"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +"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 futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "62941eff9507c8177d448bd83a44d9b9760856e184081d8cd79ba9f03dd24981" +"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" "checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" +"checksum generational-arena 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4024f96ffa0ebaaf36aa589cd41f2fd69f3a5e6fd02c86e11e12cdf41d5b46a3" "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 h2 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "910a5e7be6283a9c91b3982fa5188368c8719cce2a3cf3b86048673bf9d9c36b" "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 http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "eed324f0f0daf6ec10c474f150505af2c143f251722bf9dbd1261bd1f2ee2c1a" +"checksum httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83" "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" +"checksum hyper 0.12.25 (registry+https://github.com/rust-lang/crates.io-index)" = "7d5b6658b016965ae301fa995306db965c93677880ea70765a84235a96eae896" +"checksum hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3a800d6aa50af4b5850b2b0f659625ce9504df908e9733b635720483be26174f" +"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" "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 iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" "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.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 lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" +"checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917" +"checksum libflate 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "7346a83e8a2c3958d44d24225d905385dc31fc16e89dffb356c457b278914d20" "checksum libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3ad660d7cb8c5822cd83d10897b0f1f1526792737a179e73896152f85b88c2" +"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" "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 lz4 1.23.1 (git+https://github.com/zboxfs/lz4-rs.git)" = "" +"checksum lz4-sys 1.8.3 (git+https://github.com/zboxfs/lz4-rs.git)" = "" +"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "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 mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "3e27ca21f40a310bd06d9031785f4801710d566c184a6e15bad4f1d9b65f9425" +"checksum mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "30de2e4613efcba1ec63d8133f344076952090c122992a903359be5a4f99c3ed" +"checksum miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c468f2369f07d651a5d0bb2c9079f8488a66d5466efe42d0c5c6466edcb7f71e" +"checksum miniz_oxide_c_api 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7fe927a42e3807ef71defb191dc87d4e24479b221e67015fe38ae2b7b447bab" +"checksum mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432" +"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +"checksum native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff8e08de0070bbf4c31f452ea2a70db092f36f6f2e4d897adf5674477d488fb2" +"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" "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 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 nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" +"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" "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 openssl 0.10.20 (registry+https://github.com/rust-lang/crates.io-index)" = "5a0d6b781aac4ac1bd6cafe2a2f0ad8c16ae8e1dd5184822a16c50139f8838d9" +"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" +"checksum openssl-sys 0.9.43 (registry+https://github.com/rust-lang/crates.io-index)" = "33c86834957dd5b915623e94f2f4ab2c70dd8f6b70679824155d5ae21dbd495d" +"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 peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" +"checksum phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" +"checksum phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" +"checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" +"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" "checksum plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +"checksum podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "780fb4b6698bbf9cf2444ea5d22411cef2953f0824b98f33cf454ec5615645bd" "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.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" "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" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" @@ -1595,35 +2742,49 @@ dependencies = [ "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_syscall 0.1.52 (registry+https://github.com/rust-lang/crates.io-index)" = "d32b3053e5ced86e4bc0411fec997389532bf56b000e66cb4884eeeb41413d69" "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 regex 1.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "559008764a17de49a3146b234641644ed37d118d1ef641a0bb573d146edc6ce0" +"checksum regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "dcfd8681eebe297b81d98498869d4aae052137651ad7b96822f09ceb690d0a96" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" +"checksum reqwest 0.9.13 (registry+https://github.com/rust-lang/crates.io-index)" = "3c4ef83e0beb14bfe38b9f01330a5bc8e965a9f9628690aa28383746dac1e925" +"checksum rmp 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a3d45d7afc9b132b34a2479648863aa95c5c88e98b32285326a6ebadc80ec5c9" +"checksum rmp-serde 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)" = "011e1d58446e9fa3af7cdc1fb91295b10621d3ac4cb3a85cc86385ee9ca50cd3" "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 schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f6abf258d99c3c1c5c2131d99d064e94b7b3dd5f416483057f308fea253339" "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 security-framework 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfab8dda0e7a327c696d893df9ffa19cadc4bd195797997f5223cf5831beaf05" +"checksum security-framework-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3d6696852716b589dff9e886ff83778bb635150168e83afa8ac6b8a78cb82abc" "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.89 (registry+https://github.com/rust-lang/crates.io-index)" = "92514fb95f900c9b5126e32d020f5c6d40564c27a5ea6d1d7d9f157a96623560" +"checksum serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "aa5f7c20820475babd2c077c3ab5f8c77a31c15e16ea38687b4c02d3e48680f4" "checksum serde-bench 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "d733da87e79faaac25616e33d26299a41143fd4cd42746cbb0e91d8feea243fd" "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_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "58fc82bec244f168b23d1963b45c8bf5726e9a15a9d146a067f9081aeed2de79" "checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d" +"checksum serde_test 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce17ed207fa61e7f4701a778a6c111da84a441ca9a8f50b92808f4223dd240b" +"checksum serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d48f9f99cd749a2de71d29da5f948de7f2764cc5a9d7f3c97e3514d4ee6eabf2" +"checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" +"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "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 string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b639411d0b9c738748b5397d5ceba08e648f4f1992231aa859af1a017f31f60b" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "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 syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)" = "66c8865bf5a7cbb662d8b011950060b3c8743dca141b054bf7195b20d314d8e2" "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 target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4af5e2227f0b887d591d3724b796a96eff04226104d872f5b3883fcd427d64b9" +"checksum take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" +"checksum tar 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)" = "c2167ff53da2a661702b3299f71a91b61b1dffef36b4b2884b1f9c67254c0133" +"checksum target-lexicon 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6923974ce4eb5bd28814756256d8ab71c28dd6e7483313fe7ab6614306bf633" +"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" "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" @@ -1631,28 +2792,52 @@ dependencies = [ "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 tokio 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "65641e515a437b308ab131a82ce3042ff9795bef5d6c5a9be4eb24195c417fd9" +"checksum tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d16217cad7f1b840c5a97dfb3c43b0c871fef423a6e8d2118c604e843662a443" +"checksum tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "83ea44c6c0773cc034771693711c35c677b4b5a4b21b9e7071704c54de7d555e" +"checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926" +"checksum tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6af16bfac7e112bea8b0442542161bfc41cbfa4466b580bdda7d18cb88b911ce" +"checksum tokio-sync 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fda385df506bf7546e70872767f71e81640f1f251bdf2fd8eb81a0eaec5fe022" +"checksum tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119" +"checksum tokio-threadpool 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "ec5759cf26cf9659555f36c431b515e3d05f66831741c85b4b5d5dfb9cf1323c" +"checksum tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2910970404ba6fa78c5539126a9ae2045d62e3713041e447f695f41405a120c6" +"checksum tokio-trace-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "350c9edade9830dc185ae48ba45667a445ab59f6167ef6d0254ec9d2430d9dd3" "checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" +"checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" "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 unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" +"checksum unicase 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41d17211f887da8e4a70a45b9536f26fc5de166b81e2d5d80de4a17fd22553bd" +"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +"checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426" "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 url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" +"checksum uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a" +"checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d" "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 want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "797464475f30ddb8830cc529aaaae648d581f99e2036a928877dfde027ddf6b3" +"checksum wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)" = "981a8797cf89762e0233ec45fae731cb79a4dfaee12d9f0fe6cee01e4ac58d00" "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 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" "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" +"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +"checksum xattr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" +"checksum zbox 0.6.1 (git+https://github.com/wasmerio/zbox?branch=bundle-libsodium)" = "" +"checksum zip 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1cbbddef6339155bc4fa8e2609040078ff18f3011117b55caa9f0516d544a357" +"checksum zstd 0.4.22+zstd.1.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6f042dd18d52854d302d3d92f66d0a63c2d520d7b7034a9d43cde7441d1b4ddd" +"checksum zstd-safe 1.4.7+zstd.1.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "63febf0b0dcd076db81e6b3110ed254cfb8ed54378a4c3cfbb68956e839d4f59" +"checksum zstd-sys 1.4.8+zstd.1.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4cb187d624025a7d9878ecf13437491869423426183ded2fa40d4651b85f7ae7" diff --git a/Cargo.toml b/Cargo.toml index 946b045b3..5de0f17ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer" -version = "0.2.1" +version = "0.4.1" authors = ["The Wasmer Engineering Team "] edition = "2018" repository = "https://github.com/wasmerio/wasmer" @@ -19,26 +19,39 @@ include = [ ] [dependencies] +errno = "0.2.4" structopt = "0.2.11" wabt = "0.7.2" +hashbrown = "0.1.8" wasmer-clif-backend = { path = "lib/clif-backend" } +wasmer-singlepass-backend = { path = "lib/singlepass-backend", optional = true } +wasmer-middleware-common = { path = "lib/middleware-common" } wasmer-runtime = { path = "lib/runtime" } +wasmer-runtime-abi = { path = "lib/runtime-abi", optional = true } wasmer-runtime-core = { path = "lib/runtime-core" } wasmer-emscripten = { path = "lib/emscripten" } - -[target.'cfg(not(windows))'.dependencies] -wasmer-llvm-backend = { path = "lib/llvm-backend" } +wasmer-llvm-backend = { path = "lib/llvm-backend", optional = true } +wasmer-wasi = { path = "lib/wasi", optional = true } [workspace] -members = ["lib/clif-backend", "lib/runtime", "lib/runtime-core", "lib/emscripten", "lib/spectests", "lib/win-exception-handler", "lib/runtime-c-api", "lib/llvm-backend"] +members = ["lib/clif-backend", "lib/singlepass-backend", "lib/runtime", "lib/runtime-abi", "lib/runtime-core", "lib/emscripten", "lib/spectests", "lib/win-exception-handler", "lib/runtime-c-api", "lib/llvm-backend", "lib/wasi", "lib/middleware-common", "examples/plugin-for-example"] [build-dependencies] wabt = "0.7.2" glob = "0.2.11" +rustc_version = "0.2.3" [features] -default = ["fast-tests"] - -debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"] +default = ["fast-tests", "wasi"] +debug = ["wasmer-runtime-core/debug"] +extra-debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"] # This feature will allow cargo test to run much faster fast-tests = [] +"backend:llvm" = ["wasmer-llvm-backend"] +"backend:singlepass" = ["wasmer-singlepass-backend"] +wasi = ["wasmer-wasi"] +vfs = ["wasmer-runtime-abi"] + +[[example]] +name = "plugin" +crate-type = ["bin"] diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..02ea6c413 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,25 @@ +FROM circleci/rust:1.33.0-stretch as wasmer-build-env +RUN sudo apt-get update && \ + sudo apt-get install -y --no-install-recommends \ + cmake \ + && sudo rm -rf /var/lib/apt/lists/* +RUN curl -SL https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz \ + | tar -xJC /home/circleci +ENV LLVM_SYS_70_PREFIX /home/circleci/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/ + +FROM wasmer-build-env AS wasmer-debug-env +RUN sudo apt-get update && \ + sudo apt-get install -y --no-install-recommends \ + valgrind \ + && sudo rm -rf /var/lib/apt/lists/* + +FROM wasmer-build-env AS wasmer-build +WORKDIR /home/circleci/wasmer +COPY . /home/circleci/wasmer +RUN sudo chmod -R 777 . +RUN cargo build --release + +FROM debian:stretch AS wasmer +WORKDIR /root/ +COPY --from=wasmer-build /home/circleci/wasmer/target/release/wasmer . +ENTRYPOINT ["./wasmer"] \ No newline at end of file diff --git a/LICENSE b/LICENSE index 7e40523b0..079740dd3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,6 @@ -Copyright (c) 2019 Syrus Akbary +MIT License + +Copyright (c) 2019 Wasmer, Inc. and its affiliates. 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 0c9a0692d..2a1bfa138 100644 --- a/Makefile +++ b/Makefile @@ -25,36 +25,67 @@ integration-tests: release echo "Running Integration Tests" ./integration_tests/lua/test.sh ./integration_tests/nginx/test.sh + ./integration_tests/cowsay/test.sh lint: cargo fmt --all -- --check - cargo clippy --all + cargo +nightly-2019-02-27 clippy --all precommit: lint test +build-install: + mkdir -p ./install/bin + cp ./wapm-cli/target/release/wapm ./install/bin/ + cp ./target/release/wasmer ./install/bin/ + tar -C ./install -zcvf wasmer.tar.gz bin/wapm bin/wasmer + +# For installing the contents locally +do-install: + tar -C ~/.wasmer -zxvf wasmer.tar.gz + test: # We use one thread so the emscripten stdouts doesn't collide - cargo test --all --exclude wasmer-runtime-c-api --exclude wasmer-emscripten --exclude wasmer-spectests -- $(runargs) + cargo test --all --exclude wasmer-runtime-c-api --exclude wasmer-emscripten --exclude wasmer-spectests --exclude wasmer-singlepass-backend -- $(runargs) # cargo test --all --exclude wasmer-emscripten -- --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) +test-singlepass: + cargo test --manifest-path lib/spectests/Cargo.toml --features singlepass + +test-emscripten-llvm: cargo test --manifest-path lib/emscripten/Cargo.toml --features llvm -- --test-threads=1 $(runargs) +test-emscripten-clif: + cargo test --manifest-path lib/emscripten/Cargo.toml --features clif -- --test-threads=1 $(runargs) + +test-emscripten-singlepass: + cargo test --manifest-path lib/emscripten/Cargo.toml --features singlepass -- --test-threads=1 $(runargs) + +singlepass-debug-release: + cargo +nightly build --features "backend:singlepass debug" --release + +singlepass-release: + cargo +nightly build --features "backend:singlepass" --release + +singlepass-build: + cargo +nightly build --features "backend:singlepass debug" + 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 +production-release: + cargo build --release --features backend:singlepass,backend:llvm debug-release: cargo build --release --features "debug" +extra-debug-release: + cargo build --release --features "extra-debug" + publish-release: ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete ${VERSION} ./artifacts/ diff --git a/README.md b/README.md index 53677bca9..b1a4ccfa2 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ ## 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 [WASI](https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/) and [Emscripten](https://emscripten.org/). Install Wasmer with: @@ -26,23 +26,53 @@ Install Wasmer with: curl https://get.wasmer.io -sSfL | sh ``` -_**NEW ✨**: You can now embed Wasmer in your Rust application, check our [example repo](https://github.com/wasmerio/wasmer-rust-example) to see how!_ +Wasmer runtime can also be embedded in different languages, so you can use WebAssembly anywhere ✨: + +* [**Rust**](https://github.com/wasmerio/wasmer-rust-example) +* [**C/C++**](https://github.com/wasmerio/wasmer-c-api) +* [**🐘 PHP**](https://github.com/wasmerio/php-ext-wasm) +* [**🐍 Python**](https://github.com/wasmerio/python-ext-wasm) +* [**💎 Ruby**](https://github.com/wasmerio/ruby-ext-wasm) ### Usage 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 Lua, PHP, SQLite and nginx!_): ```sh # Run Lua wasmer run examples/lua.wasm +# Run PHP +wasmer run examples/php.wasm + +# Run SQLite +wasmer run examples/sqlite.wasm + # Run nginx wasmer run examples/nginx/nginx.wasm -- -p examples/nginx -c nginx.conf ``` +#### With WAPM + +Installing Wasmer through `wasmer.io` includes +[wapm](https://github.com/wasmerio/wapm-cli), the WebAssembly package manager. + +Wapm allows you to easily download, run, and distribute WebAssembly binaries. + +```sh +# Install cowsay globally +wapm install -g cowsay + +# Run cowsay +wapm run cowsay "Hello, world!" +``` + +For more information about wapm, check out the [website](https://www.wapm.io) +and this [example program](https://github.com/wapm-packages/rust-wasi-example). + ## Code Structure Wasmer is structured into different directories: @@ -70,6 +100,7 @@ Please select your operating system: - [macOS](#macos) - [Debian-based Linuxes](#debian-based-linuxes) +- [FreeBSD](#freebsd) - [Microsoft Windows](#windows-msvc) #### macOS @@ -89,7 +120,13 @@ sudo port install cmake #### Debian-based Linuxes ```sh -sudo apt install cmake +sudo apt install cmake pkg-config libssl-dev +``` + +#### FreeBSD + +```sh +pkg install cmake ``` #### Windows (MSVC) @@ -164,6 +201,7 @@ Below are some of the goals of this project (in order of priority): - [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)_ +- [x] Support WASI - released in [0.3.0](https://github.com/wasmerio/wasmer/releases/tag/0.3.0) - [ ] Support Emscripten calls _(in the works)_ - [ ] Support Rust ABI calls - [ ] Support Go ABI calls diff --git a/binary-name.sh b/binary-name.sh index 98a3a8497..4ac8ef8e3 100755 --- a/binary-name.sh +++ b/binary-name.sh @@ -23,8 +23,8 @@ initOS() { darwin) OS='darwin';; linux) OS='linux';; freebsd) OS='freebsd';; - mingw*) OS='windows';; - msys*) OS='windows';; + # mingw*) OS='windows';; + # msys*) OS='windows';; *) echo "OS ${OS} is not supported by this installation script"; exit 1;; esac } @@ -34,11 +34,11 @@ initArch initOS # determine install directory if required -BINARY="wasmer-${OS}-${ARCH}" - +BINARY="wasmer-${OS}-${ARCH}.tar.gz" + # add .exe if on windows -if [ "$OS" = "windows" ]; then - BINARY="$BINARY.exe" -fi +# if [ "$OS" = "windows" ]; then +# BINARY="$BINARY.exe" +# fi echo "${BINARY}" diff --git a/bors.toml b/bors.toml index c56141cd5..3ccbdc008 100644 --- a/bors.toml +++ b/bors.toml @@ -2,8 +2,9 @@ status = [ "ci/circleci: lint", "ci/circleci: test", "ci/circleci: test-macos", + "ci/circleci: test-rust-nightly", "continuous-integration/appveyor/branch" ] required_approvals = 1 timeout_sec = 900 -delete_merged_branches = true \ No newline at end of file +delete_merged_branches = true diff --git a/docs/docker.md b/docs/docker.md new file mode 100644 index 000000000..2086284f7 --- /dev/null +++ b/docs/docker.md @@ -0,0 +1,39 @@ +# Dockerfile Documentation +The `Dockerfile` included in the project root directory could be used for development purposes or to build a small image containing the `wasmer` executable. + +The `wasmer-build-env` stage in the Dockerfile contains the dependencies needed to compile Wasmer including LLVM. + +The `wasmer-debug-env` stage adds the `valgrind` profiling tool to the `wasmer-build-env` stage. + +The `wasmer-build` stage in the Dockerfile will copy the current directory, assuming the build context is the `wasmer` project, and build the project using `cargo build --release`. + +The `wasmer` stage will copy the resulting `wasmer` executable from the `wasmer-build` stage into a new base image to create a smaller image containing `wasmer`. + +## Example Usages + +### Wasmer image +1. From the `wasmer` project directory, build the image: +`docker build -t wasmer --target=wasmer .` + +2. List options: +`docker run wasmer --help` + +3. Mount a directory, and run an example wasm file: +`docker run -v /Users/admin/Documents/wasmer-workspace:/root/wasmer-workspace wasmer run /root/wasmer-workspace/examples/hello.wasm` + +### Profiling +1. Build `wasmer-debug-env`: + `docker build --tag=wasmer-debug-env --target wasmer-debug-env .` + +2. Mount a directory from the host and run interactively: + `docker run -it -v /Users/admin/Documents/wasmer-workspace:/home/circleci/wasmer-workspace wasmer-debug-env /bin/bash` + +3. Inside the container, build `wasmer` and run profiling tool: +``` +cd /home/circleci/wasmer-workspace/wasmer` +cargo build +valgrind --tool=callgrind --dump-instr=yes --collect-jumps=yes --simulate-cache=yes target/debug/wasmer run test.wasm +``` + +The `callgrind.out` can be viewed with the `qcachegrind` tool on Mac OS (`brew install qcachegrind`). + diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 000000000..5abb6eaee --- /dev/null +++ b/examples/README.md @@ -0,0 +1,9 @@ +# WebAssembly Examples + +In this directory you can find a set of different examples that can run on the Wasmer WebAssembly runtime: + +* Cowsay (WASI ABI) [[source-code](https://github.com/wapm-packages/cowsay)] [[wapm-package](https://wapm.io/package/cowsay)] +* Nginx (Emscripten ABI) [[source-code](https://github.com/wapm-packages/nginx)] [[wapm-package](https://wapm.io/package/nginx)] +* Lua (Emscripten ABI) [[source-code](https://github.com/wapm-packages/lua)] [[wapm-package](https://wapm.io/package/lua)] +* PHP (Emscripten ABI) [[source-code](https://github.com/wapm-packages/php)] [[wapm-package](https://wapm.io/package/php)] +* SQLite (Emscripten ABI) [[source-code](https://github.com/wapm-packages/sqlite)] [[wapm-package](https://wapm.io/package/sqlite)] diff --git a/examples/cowsay.wasm b/examples/cowsay.wasm new file mode 100755 index 000000000..4ff43df2c Binary files /dev/null and b/examples/cowsay.wasm differ diff --git a/examples/exit.wat b/examples/exit.wat new file mode 100644 index 000000000..b2f832cb3 --- /dev/null +++ b/examples/exit.wat @@ -0,0 +1,11 @@ +(module + (import "wasi_unstable" "proc_exit" (func $proc_exit (param i32))) + (export "_start" (func $_start)) + + (memory 10) + (export "memory" (memory 0)) + + (func $_start + (call $proc_exit (i32.const 7)) + ) +) diff --git a/examples/lua.wasm b/examples/lua.wasm index 1a424ae53..c0e65b22a 100644 Binary files a/examples/lua.wasm and b/examples/lua.wasm differ diff --git a/examples/php.wasm b/examples/php.wasm new file mode 100644 index 000000000..36c2325b9 Binary files /dev/null and b/examples/php.wasm differ diff --git a/examples/plugin-for-example.wasm b/examples/plugin-for-example.wasm new file mode 100755 index 000000000..4754287ab Binary files /dev/null and b/examples/plugin-for-example.wasm differ diff --git a/examples/plugin-for-example/Cargo.toml b/examples/plugin-for-example/Cargo.toml new file mode 100644 index 000000000..28d234910 --- /dev/null +++ b/examples/plugin-for-example/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "plugin-for-example" +version = "0.1.0" +authors = ["The Wasmer Engineering Team "] +edition = "2018" + +[dependencies] diff --git a/examples/plugin-for-example/README.md b/examples/plugin-for-example/README.md new file mode 100644 index 000000000..42bc3ac26 --- /dev/null +++ b/examples/plugin-for-example/README.md @@ -0,0 +1,43 @@ +# WASI plugin example + +In this example we extend the imports of Wasmer's WASI ABI to demonstrate how custom plugins work. + +See the `wasmer/examples/plugin.rs` file for the source code of the host system. + +## Compiling +_Attention Windows users: WASI target only works with the `nightly-x86_64-pc-windows-gnu` toolchain._ +``` +# Install an up to date version of Rust nightly +# Add the target +rustup target add wasm32-unknown-wasi +# build it +cargo +nightly build --release --target=wasm32-unknown-wasi +# copy it to examples folder +cp ../../target/wasm32-unknown-wasi/release/plugin-for-example.wasm ../ +``` + +## Running +``` +# Go back to top level Wasmer dir +cd .. +# Run the example +cargo run --example plugin +``` + +## Inspecting the plugin +``` +# Install wabt via wapm; installed globally with the `g` flag +wapm install -g wabt +# Turn the binary WASM file in to a readable WAT text file +wapm run wasm2wat examples/plugin-for-example.wasm +``` + +At the top of the file we can see which functions this plugin expects. Most are covered by WASI, but we handle the rest. + +## Explanation + +In this example, we instantiate a system with an extended (WASI)[wasi] ABI, allowing our program to rely on Wasmer's implementation of the syscalls defined by WASI as well as our own that we made. This allows us to use the full power of an existing ABI, like WASI, and give it super-powers for our specific use case. + +Because the Rust WASI doesn't support the crate type of `cdylib`, we have to include a main function which we don't use. This is being discussed [here](https://github.com/WebAssembly/WASI/issues/24). + +[wasi]: https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/ diff --git a/examples/plugin-for-example/src/main.rs b/examples/plugin-for-example/src/main.rs new file mode 100644 index 000000000..9ee284aab --- /dev/null +++ b/examples/plugin-for-example/src/main.rs @@ -0,0 +1,12 @@ +extern "C" { + fn it_works() -> i32; +} + +#[no_mangle] +pub fn plugin_entrypoint(n: i32) -> i32 { + println!("Hello from inside WASI"); + let result = unsafe { it_works() }; + result + n +} + +pub fn main() {} diff --git a/examples/plugin-for-example/wapm.toml b/examples/plugin-for-example/wapm.toml new file mode 100644 index 000000000..7e2e261a7 --- /dev/null +++ b/examples/plugin-for-example/wapm.toml @@ -0,0 +1,12 @@ +[package] +name = "plugin-for-example" +version = "0.1.0" +description = "A plugin for our example system" +readme = "README.md" +repository = "https://github.com/wasmerio/wasmer/examples/plugin-for-example" +license = "MIT" + +[[module]] +name = "plugin-for-example" +source = "../../target/wasm32-unknown-wasi/release/plugin-for-example.wasm" +abi = "none" \ No newline at end of file diff --git a/examples/plugin.rs b/examples/plugin.rs new file mode 100644 index 000000000..dbb06fe61 --- /dev/null +++ b/examples/plugin.rs @@ -0,0 +1,38 @@ +use wasmer_runtime::{func, imports, instantiate}; +use wasmer_runtime_core::vm::Ctx; +use wasmer_wasi::generate_import_object; + +static PLUGIN_LOCATION: &'static str = "examples/plugin-for-example.wasm"; + +fn it_works(_ctx: &mut Ctx) -> i32 { + println!("Hello from outside WASI"); + 5 +} + +fn main() { + // Load the plugin data + let wasm_bytes = std::fs::read(PLUGIN_LOCATION).expect(&format!( + "Could not read in WASM plugin at {}", + PLUGIN_LOCATION + )); + + // WASI imports + let mut base_imports = generate_import_object(vec![], vec![], vec![]); + // env is the default namespace for extern functions + let custom_imports = imports! { + "env" => { + "it_works" => func!(it_works), + }, + }; + // The WASI imports object contains all required import functions for a WASI module to run. + // Extend this imports with our custom imports containing "it_works" function so that our custom wasm code may run. + base_imports.extend(custom_imports); + let instance = + instantiate(&wasm_bytes[..], &base_imports).expect("failed to instantiate wasm module"); + + // get a reference to the function "plugin_entrypoint" which takes an i32 and returns an i32 + let entry_point = instance.func::<(i32), i32>("plugin_entrypoint").unwrap(); + // call the "entry_point" function in WebAssembly with the number "2" as the i32 argument + let result = entry_point.call(2).expect("failed to execute plugin"); + println!("result: {}", result); +} diff --git a/examples/single_pass_tests/br_table.wat b/examples/single_pass_tests/br_table.wat new file mode 100644 index 000000000..72e8f92fe --- /dev/null +++ b/examples/single_pass_tests/br_table.wat @@ -0,0 +1,37 @@ +(module + (func $main (export "main") + (i32.eq (call $test (i32.const 0)) (i32.const 2)) + (i32.eq (call $test (i32.const 1)) (i32.const 0)) + (i32.eq (call $test (i32.const 2)) (i32.const 1)) + (i32.eq (call $test (i32.const 3)) (i32.const 3)) + (i32.eq (call $test (i32.const 4)) (i32.const 3)) + (i32.and) + (i32.and) + (i32.and) + (i32.and) + (i32.const 1) + (i32.eq) + (br_if 0) + (unreachable) + ) + + (func $test (param $p i32) (result i32) + (block + (block + (block + (block + (block + (get_local $p) + (br_table 2 0 1 3) + ) + (return (i32.const 0)) + ) + (return (i32.const 1)) + ) + (return (i32.const 2)) + ) + (return (i32.const 3)) + ) + (unreachable) + ) +) diff --git a/examples/single_pass_tests/call.wat b/examples/single_pass_tests/call.wat new file mode 100644 index 000000000..2986ac11a --- /dev/null +++ b/examples/single_pass_tests/call.wat @@ -0,0 +1,23 @@ +(module + (func $main (export "main") + (local $a i32) + (block + (set_local $a (i32.const 33)) + (i32.const 11) + (call $foo (get_local $a)) + (i32.add) + (i32.const 86) + (i32.eq) + (br_if 0) + (unreachable) + ) + ) + + (func $foo (param $input i32) (result i32) + (local $a i32) + (set_local $a (i32.const 42)) + (get_local $a) + (get_local $input) + (i32.add) + ) +) diff --git a/examples/single_pass_tests/call_indirect.wat b/examples/single_pass_tests/call_indirect.wat new file mode 100644 index 000000000..019f045e8 --- /dev/null +++ b/examples/single_pass_tests/call_indirect.wat @@ -0,0 +1,25 @@ +(module + (type $binop (func (param i32 i32) (result i32))) + (table 1 100 anyfunc) + (elem (i32.const 5) $sub) + (elem (i32.const 10) $add) + + (func $main (export "main") + (if (i32.eq (call_indirect (type $binop) (i32.const 42) (i32.const 1) (i32.const 10)) (i32.const 43)) + (then) + (else unreachable) + ) + (if (i32.eq (call_indirect (type $binop) (i32.const 42) (i32.const 1) (i32.const 5)) (i32.const 41)) + (then) + (else unreachable) + ) + ) + + (func $add (param i32) (param i32) (result i32) + (i32.add (get_local 0) (get_local 1)) + ) + + (func $sub (param i32) (param i32) (result i32) + (i32.sub (get_local 0) (get_local 1)) + ) +) diff --git a/examples/single_pass_tests/div.wat b/examples/single_pass_tests/div.wat new file mode 100644 index 000000000..3ef5d6e53 --- /dev/null +++ b/examples/single_pass_tests/div.wat @@ -0,0 +1,36 @@ +(module + (func $main (export "main") + (i32.const 1) + (if (i32.ne (i32.div_s (i32.const 2) (i32.const -1)) (i32.const -2)) + (then unreachable) + ) + (i32.const 2) + (if (i32.ne (i32.div_u (i32.const 2) (i32.const -1)) (i32.const 0)) + (then unreachable) + ) + (i32.const 3) + (if (i32.ne (i32.div_u (i32.const 10) (i32.const 5)) (i32.const 2)) + (then unreachable) + ) + (i32.const 4) + (if (i64.ne (i64.div_s (i64.const 300000000000) (i64.const -1)) (i64.const -300000000000)) + (then unreachable) + ) + (i32.const 5) + (if (i64.ne (i64.div_u (i64.const 300000000000) (i64.const -1)) (i64.const 0)) + (then unreachable) + ) + (i32.const 6) + (if (i64.ne (i64.div_u (i64.const 300000000000) (i64.const 2)) (i64.const 150000000000)) + (then unreachable) + ) + (i32.add) + (i32.add) + (i32.add) + (i32.add) + (i32.add) + (if (i32.ne (i32.const 21)) + (then unreachable) + ) + ) +) diff --git a/examples/single_pass_tests/global.wat b/examples/single_pass_tests/global.wat new file mode 100644 index 000000000..f06e15302 --- /dev/null +++ b/examples/single_pass_tests/global.wat @@ -0,0 +1,26 @@ +(module + (global $g1 (mut i32) (i32.const 0)) + (global $g2 (mut i32) (i32.const 99)) + (func $main (export "main") + (if (i32.eq (get_global $g1) (i32.const 0)) + (then) + (else unreachable) + ) + (if (i32.eq (get_global $g2) (i32.const 99)) + (then) + (else unreachable) + ) + + (set_global $g1 (i32.add (get_global $g1) (i32.const 1))) + (set_global $g2 (i32.sub (get_global $g2) (i32.const 1))) + + (if (i32.eq (get_global $g1) (i32.const 1)) + (then) + (else unreachable) + ) + (if (i32.eq (get_global $g2) (i32.const 98)) + (then) + (else unreachable) + ) + ) +) diff --git a/examples/single_pass_tests/i32.wat b/examples/single_pass_tests/i32.wat new file mode 100644 index 000000000..66daadcc9 --- /dev/null +++ b/examples/single_pass_tests/i32.wat @@ -0,0 +1,44 @@ +(module + (func $main (export "main") (result i32) + (local $v1 i32) + (block + (i32.const 10) + (set_local $v1) + + (i32.const 42) + (get_local $v1) + (i32.add) + (i32.const 53) + (i32.eq) + (br_if 0) + + (i32.const 1) + (i32.const -100) + (i32.const 41) + (i32.lt_s) + (i32.sub) + (br_if 0) + + (i32.const -100) + (i32.const 41) + (i32.lt_u) + (br_if 0) + + (i32.const 1) + (i32.const 100) + (i32.const -41) + (i32.gt_s) + (i32.sub) + (br_if 0) + + (i32.const 100) + (i32.const -41) + (i32.gt_u) + (br_if 0) + + (i32.const 0) + (return) + ) + (unreachable) + ) +) diff --git a/examples/single_pass_tests/i64.wat b/examples/single_pass_tests/i64.wat new file mode 100644 index 000000000..ebe10eb99 --- /dev/null +++ b/examples/single_pass_tests/i64.wat @@ -0,0 +1,48 @@ +(module + (func $main (export "main") (result i64) + (local $v1 i64) + (block + (i64.const 10) + (set_local $v1) + + (i64.const 42) + (get_local $v1) + (i64.add) + (i64.const 53) + (i64.eq) + (br_if 0) + + (i64.const 1) + (i64.const -100) + (i64.const 41) + (i64.lt_s) + (i64.extend_u/i32) + (i64.sub) + (i32.wrap/i64) + (br_if 0) + + (i64.const -100) + (i64.const 41) + (i64.lt_u) + (br_if 0) + + (i64.const 1) + (i64.const 100) + (i64.const -41) + (i64.gt_s) + (i64.extend_u/i32) + (i64.sub) + (i32.wrap/i64) + (br_if 0) + + (i64.const 100) + (i64.const -41) + (i64.gt_u) + (br_if 0) + + (i64.const 0) + (return) + ) + (unreachable) + ) +) diff --git a/examples/single_pass_tests/if_else.wat b/examples/single_pass_tests/if_else.wat new file mode 100644 index 000000000..533b8f13d --- /dev/null +++ b/examples/single_pass_tests/if_else.wat @@ -0,0 +1,33 @@ +(module + (func $main (export "main") + (local $a i32) + (set_local $a (i32.const 33)) + + (block + (call $foo (if (result i32) (i32.eq (get_local $a) (i32.const 33)) + (then (i32.const 1)) + (else (i32.const 2)) + )) + (i32.eq (i32.const 43)) + (br_if 0) + (unreachable) + ) + (block + (call $foo (if (result i32) (i32.eq (get_local $a) (i32.const 30)) + (then (i32.const 1)) + (else (i32.const 2)) + )) + (i32.eq (i32.const 44)) + (br_if 0) + (unreachable) + ) + ) + + (func $foo (param $input i32) (result i32) + (local $a i32) + (set_local $a (i32.const 42)) + (get_local $a) + (get_local $input) + (i32.add) + ) +) diff --git a/examples/single_pass_tests/loop.wat b/examples/single_pass_tests/loop.wat new file mode 100644 index 000000000..4d7c7eac8 --- /dev/null +++ b/examples/single_pass_tests/loop.wat @@ -0,0 +1,17 @@ +(module + (func $main (export "main") + (local $count i32) + (local $sum i32) + (loop (result i32) + (set_local $count (i32.add (get_local $count) (i32.const 1))) + (set_local $sum (i32.add (get_local $sum) (get_local $count))) + (i32.sub (i32.const 1) (i32.eq + (get_local $count) + (i32.const 50000) + )) + (br_if 0) + (get_local $sum) + ) + (if (i32.ne (i32.const 1250025000)) (unreachable)) + ) +) diff --git a/examples/single_pass_tests/memory.wat b/examples/single_pass_tests/memory.wat new file mode 100644 index 000000000..9c15eb1ea --- /dev/null +++ b/examples/single_pass_tests/memory.wat @@ -0,0 +1,90 @@ +(module + (memory 1) + (func $main (export "main") + (call $test_stack_layout) + ) + + (func $test_stack_layout + (local $addr i32) + (set_local $addr (i32.const 16)) + + (i32.store (get_local $addr) (i32.const 10)) + (if (i32.eq (i32.load (i32.const 14)) (i32.const 655360)) + (then) + (else (unreachable)) + ) + + (i32.const 1) + (i32.store (get_local $addr) (i32.const 11)) + (if (i32.eq (i32.load (i32.const 14)) (i32.const 720896)) + (then) + (else (unreachable)) + ) + + (i32.const 1) + (i32.store (get_local $addr) (i32.const 12)) + (if (i32.eq (i32.load (i32.const 14)) (i32.const 786432)) + (then) + (else (unreachable)) + ) + + (i32.const 1) + (i32.store (get_local $addr) (i32.const 13)) + (if (i32.eq (i32.load (i32.const 14)) (i32.const 851968)) + (then) + (else (unreachable)) + ) + + (i32.const 1) + (i32.store (get_local $addr) (i32.const 14)) + (if (i32.eq (i32.load (i32.const 14)) (i32.const 917504)) + (then) + (else (unreachable)) + ) + + (i32.const 1) + (i32.store (get_local $addr) (i32.const 15)) + (if (i32.eq (i32.load (i32.const 14)) (i32.const 983040)) + (then) + (else (unreachable)) + ) + + (i32.const 1) + (i32.store (get_local $addr) (i32.const 16)) + (if (i32.eq (i32.load (i32.const 14)) (i32.const 1048576)) + (then) + (else (unreachable)) + ) + + (i32.const 1) + (i32.store (get_local $addr) (i32.const 17)) + (if (i32.eq (i32.load (i32.const 14)) (i32.const 1114112)) + (then) + (else (unreachable)) + ) + + (i32.const 1) + (i32.store (get_local $addr) (i32.const 18)) + (if (i32.eq (i32.load (i32.const 14)) (i32.const 1179648)) + (then) + (else (unreachable)) + ) + + (i32.const 1) + (i32.store (get_local $addr) (i32.const 19)) + (if (i32.eq (i32.load (i32.const 14)) (i32.const 1245184)) + (then) + (else (unreachable)) + ) + + (drop) + (drop) + (drop) + (drop) + (drop) + (drop) + (drop) + (drop) + (drop) + ) +) diff --git a/examples/single_pass_tests/select.wat b/examples/single_pass_tests/select.wat new file mode 100644 index 000000000..cfdd36848 --- /dev/null +++ b/examples/single_pass_tests/select.wat @@ -0,0 +1,20 @@ +(module + (func $main (export "main") + (if (i32.eq (select + (i32.const 10) + (i32.const 20) + (i32.const 1) + ) (i32.const 10)) + (then) + (else (unreachable)) + ) + (if (i32.eq (select + (i32.const 10) + (i32.const 20) + (i32.const 0) + ) (i32.const 20)) + (then) + (else (unreachable)) + ) + ) +) diff --git a/examples/single_pass_tests/tee_local.wat b/examples/single_pass_tests/tee_local.wat new file mode 100644 index 000000000..70b9e4737 --- /dev/null +++ b/examples/single_pass_tests/tee_local.wat @@ -0,0 +1,11 @@ +(module + (func $main (export "main") + (local $x i32) + (tee_local $x (i32.const 3)) + (i32.add (i32.const 4)) + (if (i32.eq (i32.const 7)) + (then) + (else unreachable) + ) + ) +) diff --git a/examples/single_pass_tests/unwinding.wat b/examples/single_pass_tests/unwinding.wat new file mode 100644 index 000000000..165179808 --- /dev/null +++ b/examples/single_pass_tests/unwinding.wat @@ -0,0 +1,38 @@ +(module + (func $main (export "main") + (i32.const 5) + (block (result i32) + (i32.const 10) + (block + (i32.const 20) + (block + (i32.const 50) + (br 1) + ) + (unreachable) + ) + ) + (i32.add) + (if (i32.eq (i32.const 15)) + (then) + (else unreachable) + ) + + (block (result i32) + (i32.const 10) + (block (result i32) + (i32.const 20) + (block + (i32.const 50) + (br 1) + ) + (unreachable) + ) + (i32.add) + ) + (if (i32.eq (i32.const 60)) + (then) + (else unreachable) + ) + ) +) diff --git a/examples/sqlite.wasm b/examples/sqlite.wasm new file mode 100644 index 000000000..9dccfb125 Binary files /dev/null and b/examples/sqlite.wasm differ diff --git a/install.sh b/install.sh index 0b7da4ca2..8d4e1e9d2 100755 --- a/install.sh +++ b/install.sh @@ -32,6 +32,7 @@ white="\033[37m" bold="\e[1m" dim="\e[2m" +# Warning: Remove this on the public repo RELEASES_URL="https://github.com/wasmerio/wasmer/releases" wasmer_download_json() { @@ -129,11 +130,11 @@ wasmer_detect_profile() { 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 WASMER_CACHE_DIR=\"\$WASMER_DIR/cache\"\nexport PATH=\"\$HOME/.wasmer/bin:\$PATH\"\n" + LOAD_STR="\n# Wasmer\nexport WASMER_DIR=\"$INSTALL_DIRECTORY\"\n[ -s \"\$WASMER_DIR/wasmer.sh\" ] && source \"\$WASMER_DIR/wasmer.sh\" # This loads wasmer\n" + SOURCE_STR="# Wasmer config\nexport WASMER_DIR=\"$INSTALL_DIRECTORY\"\nexport WASMER_CACHE_DIR=\"\$WASMER_DIR/cache\"\nexport PATH=\"\$WASMER_DIR/bin:\$WASMER_DIR/globals/wapm_packages/.bin:\$PATH\"\n" # We create the wasmer.sh file - printf "$SOURCE_STR" > "$HOME/.wasmer/wasmer.sh" + printf "$SOURCE_STR" > "$INSTALL_DIRECTORY/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" @@ -155,13 +156,15 @@ wasmer_link() { echo "If this isn't the profile of your current shell then please add the following to your correct profile:" printf "$LOAD_STR$reset\n" - version=`$HOME/.wasmer/bin/wasmer --version` || ( + version=`$INSTALL_DIRECTORY/bin/wasmer --version` || ( printf "$red> wasmer was installed, but doesn't seem to be working :($reset\n" exit 1; ) - printf "$green> Successfully installed $version!\n\n${reset}If you want to have the command available now please execute:\nsource $HOME/.wasmer/wasmer.sh$reset\n" - printf "\nOtherwise, wasmer will be available the next time you open the terminal.\n" + printf "$green> Successfully installed $version!\n\n${reset}If you want to have the command available now please execute:\nsource $INSTALL_DIRECTORY/wasmer.sh$reset\n" + printf "\nOtherwise, wasmer and wapm will be available the next time you open the terminal.\n" + echo "Note: during the alpha release of wapm, telemetry is enabled by default; if you would like to opt out, run \`wapm config set telemetry.enabled false\`." + echo "If you notice anything wrong or have any issues, please file a bug at https://github.com/wasmerio/wapm-cli :)" fi } @@ -254,7 +257,7 @@ wasmer_install() { " fi -# if [ -d "$HOME/.wasmer" ]; then +# if [ -d "$INSTALL_DIRECTORY" ]; then # if which wasmer; then # local latest_url # local specified_version @@ -283,15 +286,14 @@ wasmer_install() { # exit 0 # else # printf "$yellow> $wasmer_alt_version is already installed, Specified version: $specified_version.$reset\n" -# rm -rf "$HOME/.wasmer" +# rm -rf "$INSTALL_DIRECTORY" # fi # else -# printf "$red> $HOME/.wasmer already exists, possibly from a past Wasmer install.$reset\n" -# printf "$red> Remove it (rm -rf $HOME/.wasmer) and run this script again.$reset\n" +# printf "$red> $INSTALL_DIRECTORY already exists, possibly from a past Wasmer install.$reset\n" +# printf "$red> Remove it (rm -rf $INSTALL_DIRECTORY) and run this script again.$reset\n" # exit 0 # fi # fi - wasmer_download # $1 $2 wasmer_link wasmer_reset @@ -369,12 +371,12 @@ wasmer_download() { WASMER=INSTALL_DIRECTORY # assemble expected release artifact name - BINARY="wasmer-${OS}-${ARCH}" + BINARY="wasmer-${OS}-${ARCH}.tar.gz" # add .exe if on windows - if [ "$OS" = "windows" ]; then - BINARY="$BINARY.exe" - fi + # if [ "$OS" = "windows" ]; then + # BINARY="$BINARY.exe" + # fi # if WASMER_RELEASE_TAG was not provided, assume latest if [ -z "$WASMER_RELEASE_TAG" ]; then @@ -417,9 +419,6 @@ wasmer_download() { printf "\033[K\n\033[1A" # printf "\033[1A$cyan> Downloaded$reset\033[K\n" # echo "Setting executable permissions." - chmod +x "$DOWNLOAD_FILE" - - INSTALL_NAME="wasmer" # windows not supported yet # if [ "$OS" = "windows" ]; then @@ -428,8 +427,9 @@ wasmer_download() { # echo "Moving executable to $INSTALL_DIRECTORY/$INSTALL_NAME" - mkdir -p $INSTALL_DIRECTORY/bin - mv "$DOWNLOAD_FILE" "$INSTALL_DIRECTORY/bin/$INSTALL_NAME" + mkdir -p $INSTALL_DIRECTORY + # Untar the wasmer contents in the install directory + tar -C $INSTALL_DIRECTORY -zxvf $DOWNLOAD_FILE } wasmer_verify_or_quit() { diff --git a/integration_tests/cowsay/README.md b/integration_tests/cowsay/README.md new file mode 100644 index 000000000..f44e6e680 --- /dev/null +++ b/integration_tests/cowsay/README.md @@ -0,0 +1,9 @@ +# `cowsay` integration test + + +This starts Wasmer with the Cowsay WASI Wasm file. The test makes assertions on +the output of Wasmer. Run test with: + +```bash +./integration_tests/cowsay/test.sh +``` diff --git a/integration_tests/cowsay/test.sh b/integration_tests/cowsay/test.sh new file mode 100755 index 000000000..20c46a259 --- /dev/null +++ b/integration_tests/cowsay/test.sh @@ -0,0 +1,14 @@ +#! /bin/bash + +nohup ./target/release/wasmer run examples/cowsay.wasm --disable-cache -- "hello integration" + +if grep "hello integration" ./nohup.out +then + echo "cowsay wasi integration test succeeded" + rm ./nohup.out + exit 0 +else + echo "cowsay wasi integration test failed" + rm ./nohup.out + exit -1 +fi diff --git a/lib/README.md b/lib/README.md index 47f3264e7..83a335588 100644 --- a/lib/README.md +++ b/lib/README.md @@ -22,7 +22,8 @@ The integration builds on the Wasmer runtime and allow us to run WebAssembly fil 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). +- [WASI](./wasi): run WebAssembly files with the [WASI ABI](https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/). +- [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: _research period, see [tracking issue](https://github.com/wasmerio/wasmer/issues/97)_ @@ -33,5 +34,6 @@ to tune the codegen properties (compile speed, performance, etc) to best fit the Currently, we support multiple backends for compiling WebAssembly to machine code: -- [clif-backend](./clif-backend/): Cranelift backend -- [llvm-backend](./llvm-backend/): LLVM backend +- [singlepass-backend](./singlepass-backend/): Single pass backend - super fast compilation, slower runtime speed +- [clif-backend](./clif-backend/): Cranelift backend - slower compilation, normal runtime speed +- [llvm-backend](./llvm-backend/): LLVM backend - slow compilation, native runtime speed diff --git a/lib/clif-backend/Cargo.toml b/lib/clif-backend/Cargo.toml index 3ac5105cf..cbfbfb44a 100644 --- a/lib/clif-backend/Cargo.toml +++ b/lib/clif-backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-clif-backend" -version = "0.2.0" +version = "0.4.1" description = "Wasmer runtime Cranelift compiler backend" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -8,14 +8,14 @@ repository = "https://github.com/wasmerio/wasmer" edition = "2018" [dependencies] -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" -cranelift-wasm = "0.26.0" +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } +cranelift-native = "0.30.0" +cranelift-codegen = "0.30.0" +cranelift-entity = "0.30.0" +cranelift-wasm = "0.30.0" hashbrown = "0.1" -target-lexicon = "0.2.0" -wasmparser = "0.23.0" +target-lexicon = "0.3.0" +wasmparser = "0.29.2" byteorder = "1" nix = "0.13.0" libc = "0.2.49" @@ -33,7 +33,7 @@ version = "0.0.7" [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" } +wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.4.1" } [features] debug = ["wasmer-runtime-core/debug"] diff --git a/lib/clif-backend/README.md b/lib/clif-backend/README.md new file mode 100644 index 000000000..eb0c17e8c --- /dev/null +++ b/lib/clif-backend/README.md @@ -0,0 +1,31 @@ +

+ + Wasmer logo + +

+ +

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

+ +# Wasmer Cranelift backend + +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 represents the Cranelift backend. diff --git a/lib/clif-backend/src/cache.rs b/lib/clif-backend/src/cache.rs index 5af708703..b4f647372 100644 --- a/lib/clif-backend/src/cache.rs +++ b/lib/clif-backend/src/cache.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use wasmer_runtime_core::{ backend::{sys::Memory, CacheGen}, cache::{Artifact, Error}, - module::{ModuleInfo, ModuleInner}, + module::ModuleInfo, structures::Map, types::{LocalFuncIndex, SigIndex}, }; @@ -27,18 +27,12 @@ impl CacheGenerator { } impl CacheGen for CacheGenerator { - fn generate_cache( - &self, - module: &ModuleInner, - ) -> Result<(Box, Box<[u8]>, Memory), Error> { - let info = Box::new(module.info.clone()); - + fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), Error> { // 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, )) diff --git a/lib/clif-backend/src/lib.rs b/lib/clif-backend/src/lib.rs index 88de51e12..50fbe5dbf 100644 --- a/lib/clif-backend/src/lib.rs +++ b/lib/clif-backend/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + mod cache; mod func_env; mod libcalls; @@ -16,7 +18,7 @@ use target_lexicon::Triple; use wasmer_runtime_core::cache::{Artifact, Error as CacheError}; use wasmer_runtime_core::{ - backend::{Compiler, Token}, + backend::{Compiler, CompilerConfig, Token}, error::{CompileError, CompileResult}, module::ModuleInner, }; @@ -39,12 +41,17 @@ impl CraneliftCompiler { impl Compiler for CraneliftCompiler { /// Compiles wasm binary to a wasmer module. - fn compile(&self, wasm: &[u8], _: Token) -> CompileResult { + fn compile( + &self, + wasm: &[u8], + compiler_config: CompilerConfig, + _: Token, + ) -> CompileResult { validate(wasm)?; let isa = get_isa(); - let mut module = module::Module::new(); + let mut module = module::Module::new(&compiler_config); let module_env = module_env::ModuleEnv::new(&mut module, &*isa); let func_bodies = module_env.translate(wasm)?; diff --git a/lib/clif-backend/src/module.rs b/lib/clif-backend/src/module.rs index 9e934ee0a..ad0576e59 100644 --- a/lib/clif-backend/src/module.rs +++ b/lib/clif-backend/src/module.rs @@ -10,7 +10,7 @@ use std::sync::Arc; use wasmer_runtime_core::cache::{Artifact, Error as CacheError}; use wasmer_runtime_core::{ - backend::Backend, + backend::{Backend, CompilerConfig}, error::CompileResult, module::{ModuleInfo, ModuleInner, StringTable}, structures::{Map, TypedIndex}, @@ -25,7 +25,7 @@ pub struct Module { } impl Module { - pub fn new() -> Self { + pub fn new(compiler_config: &CompilerConfig) -> Self { Self { info: ModuleInfo { memories: Map::new(), @@ -50,6 +50,9 @@ impl Module { namespace_table: StringTable::new(), name_table: StringTable::new(), + em_symbol_map: compiler_config.symbol_map.clone(), + + custom_sections: HashMap::new(), }, } } @@ -70,16 +73,15 @@ impl Module { handler_data.clone(), )?; - let protected_caller = Caller::new(&self.info, handler_data, trampolines); - let cache_gen = Box::new(CacheGenerator::new( backend_cache, Arc::clone(&func_resolver.memory), )); + let runnable_module = Caller::new(handler_data, trampolines, func_resolver); + Ok(ModuleInner { - func_resolver: Box::new(func_resolver), - protected_caller: Box::new(protected_caller), + runnable_module: Box::new(runnable_module), cache_gen, info: self.info, @@ -100,16 +102,15 @@ impl Module { ) .map_err(|e| CacheError::Unknown(format!("{:?}", e)))?; - let protected_caller = Caller::new(&info, handler_data, trampolines); - let cache_gen = Box::new(CacheGenerator::new( backend_cache, Arc::clone(&func_resolver.memory), )); + let runnable_module = Caller::new(handler_data, trampolines, func_resolver); + Ok(ModuleInner { - func_resolver: Box::new(func_resolver), - protected_caller: Box::new(protected_caller), + runnable_module: Box::new(runnable_module), cache_gen, info, @@ -148,8 +149,8 @@ convert_clif_to_runtime_index![ (SignatureIndex: SigIndex), ]; -impl<'a> From> for FuncSig { - fn from(signature: Converter<&'a ir::Signature>) -> Self { +impl From> for FuncSig { + fn from(signature: Converter) -> Self { FuncSig::new( signature .0 diff --git a/lib/clif-backend/src/module_env.rs b/lib/clif-backend/src/module_env.rs index 7c6cd6731..7cf1dfaff 100644 --- a/lib/clif-backend/src/module_env.rs +++ b/lib/clif-backend/src/module_env.rs @@ -50,6 +50,20 @@ impl<'module, 'isa> ModuleEnv<'module, 'isa> { Ok(self.func_bodies) } + + /// Return the global for the given global index. + pub fn get_global(&self, global_index: cranelift_wasm::GlobalIndex) -> &cranelift_wasm::Global { + &self.globals[Converter(global_index).into()] + } + + /// Return the signature index for the given function index. + pub fn get_func_type( + &self, + func_index: cranelift_wasm::FuncIndex, + ) -> cranelift_wasm::SignatureIndex { + let sig_index: SigIndex = self.module.info.func_assoc[Converter(func_index).into()]; + Converter(sig_index).into() + } } impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> { @@ -59,16 +73,11 @@ 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) { + fn declare_signature(&mut self, sig: ir::Signature) { self.signatures.push(sig.clone()); self.module.info.signatures.push(Converter(sig).into()); } - /// Return the signature with the given index. - fn get_signature(&self, clif_sig_index: cranelift_wasm::SignatureIndex) -> &ir::Signature { - &self.signatures[Converter(clif_sig_index).into()] - } - /// Declares a function import to the environment. fn declare_func_import( &mut self, @@ -92,11 +101,6 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> }); } - /// Return the number of imported funcs. - fn get_num_func_imports(&self) -> usize { - self.module.info.imported_functions.len() - } - /// Declares the type (signature) of a local function in the module. fn declare_func_type(&mut self, clif_sig_index: cranelift_wasm::SignatureIndex) { // We convert the cranelift signature index to @@ -106,15 +110,6 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> self.module.info.func_assoc.push(sig_index); } - /// Return the signature index for the given function index. - fn get_func_type( - &self, - func_index: cranelift_wasm::FuncIndex, - ) -> cranelift_wasm::SignatureIndex { - let sig_index: SigIndex = self.module.info.func_assoc[Converter(func_index).into()]; - Converter(sig_index).into() - } - /// Declares a global to the environment. fn declare_global(&mut self, global: cranelift_wasm::Global) { let desc = GlobalDescriptor { @@ -180,11 +175,6 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> self.globals.push(global); } - /// Return the global for the given global index. - fn get_global(&self, global_index: cranelift_wasm::GlobalIndex) -> &cranelift_wasm::Global { - &self.globals[Converter(global_index).into()] - } - /// Declares a table to the environment. fn declare_table(&mut self, table: cranelift_wasm::Table) { use cranelift_wasm::TableElementType; @@ -238,7 +228,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> table_index: cranelift_wasm::TableIndex, base: Option, offset: usize, - elements: Vec, + elements: Box<[cranelift_wasm::FuncIndex]>, ) { // Convert Cranelift GlobalIndex to wamser GlobalIndex // let base = base.map(|index| WasmerGlobalIndex::new(index.index())); @@ -376,7 +366,11 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> } /// Provides the contents of a function body. - fn define_function_body(&mut self, body_bytes: &'data [u8]) -> cranelift_wasm::WasmResult<()> { + fn define_function_body( + &mut self, + body_bytes: &'data [u8], + body_offset: usize, + ) -> cranelift_wasm::WasmResult<()> { let mut func_translator = FuncTranslator::new(); let func_body = { @@ -390,7 +384,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> let mut func = ir::Function::with_name_signature(name, sig); - func_translator.translate(body_bytes, &mut func, &mut func_env)?; + func_translator.translate(body_bytes, body_offset, &mut func, &mut func_env)?; #[cfg(feature = "debug")] { @@ -530,7 +524,10 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> .special_param(ir::ArgumentPurpose::VMContext) .expect("missing vmctx parameter"); - let func_index = pos.ins().iconst(ir::types::I32, func_index.index() as i64); + let func_index = pos.ins().iconst( + ir::types::I32, + func_index.index() as i64 + self.module.info.imported_functions.len() as i64, + ); pos.ins().call(start_debug, &[vmctx, func_index]); diff --git a/lib/clif-backend/src/relocation.rs b/lib/clif-backend/src/relocation.rs index 92ef0485a..50a032095 100644 --- a/lib/clif-backend/src/relocation.rs +++ b/lib/clif-backend/src/relocation.rs @@ -224,6 +224,7 @@ pub enum TrapCode { IntegerDivisionByZero, BadConversionToInteger, Interrupt, + UnreachableCodeReached, User(u16), } @@ -297,6 +298,7 @@ impl binemit::TrapSink for LocalTrapSink { ir::TrapCode::IntegerDivisionByZero => TrapCode::IntegerDivisionByZero, ir::TrapCode::BadConversionToInteger => TrapCode::BadConversionToInteger, ir::TrapCode::Interrupt => TrapCode::Interrupt, + ir::TrapCode::UnreachableCodeReached => TrapCode::UnreachableCodeReached, ir::TrapCode::User(x) => TrapCode::User(x), }; diff --git a/lib/clif-backend/src/resolver.rs b/lib/clif-backend/src/resolver.rs index 94713e0b2..a55bf56d7 100644 --- a/lib/clif-backend/src/resolver.rs +++ b/lib/clif-backend/src/resolver.rs @@ -21,7 +21,6 @@ use wasmer_runtime_core::cache::Error as CacheError; use wasmer_runtime_core::{ self, backend::{ - self, sys::{Memory, Protect}, SigRegistry, }, @@ -357,13 +356,8 @@ pub struct FuncResolver { pub(crate) memory: Arc, } -// Implements FuncResolver trait. -impl backend::FuncResolver for FuncResolver { - fn get( - &self, - _module: &wasmer_runtime_core::module::ModuleInner, - index: LocalFuncIndex, - ) -> Option> { +impl FuncResolver { + pub fn lookup(&self, index: LocalFuncIndex) -> Option> { lookup_func(&self.map, &self.memory, index) } } @@ -374,20 +368,26 @@ fn round_up(n: usize, multiple: usize) -> usize { } extern "C" fn i32_print(_ctx: &mut vm::Ctx, n: i32) { - print!(" i32: {},", n); + eprint!(" i32: {},", n); } extern "C" fn i64_print(_ctx: &mut vm::Ctx, n: i64) { - print!(" i64: {},", n); + eprint!(" i64: {},", n); } extern "C" fn f32_print(_ctx: &mut vm::Ctx, n: f32) { - print!(" f32: {},", n); + eprint!(" f32: {},", n); } extern "C" fn f64_print(_ctx: &mut vm::Ctx, n: f64) { - print!(" f64: {},", n); + eprint!(" f64: {},", n); } -extern "C" fn start_debug(_ctx: &mut vm::Ctx, func_index: u32) { - print!("func ({}), args: [", func_index); +extern "C" fn start_debug(ctx: &mut vm::Ctx, func_index: u32) { + if let Some(symbol_map) = unsafe { ctx.borrow_symbol_map() } { + if let Some(fn_name) = symbol_map.get(&func_index) { + eprint!("func ({} ({})), args: [", fn_name, func_index); + return; + } + } + eprint!("func ({}), args: [", func_index); } extern "C" fn end_debug(_ctx: &mut vm::Ctx) { - println!(" ]"); + eprintln!(" ]"); } diff --git a/lib/clif-backend/src/signal/mod.rs b/lib/clif-backend/src/signal/mod.rs index 04b97e832..3facce2ad 100644 --- a/lib/clif-backend/src/signal/mod.rs +++ b/lib/clif-backend/src/signal/mod.rs @@ -1,15 +1,14 @@ use crate::relocation::{TrapData, TrapSink}; +use crate::resolver::FuncResolver; use crate::trampoline::Trampolines; -use hashbrown::HashSet; use libc::c_void; -use std::{any::Any, cell::Cell, sync::Arc}; +use std::{any::Any, cell::Cell, ptr::NonNull, sync::Arc}; use wasmer_runtime_core::{ - backend::{ProtectedCaller, Token, UserTrapper}, - error::RuntimeResult, - export::Context, - module::{ExportIndex, ModuleInfo, ModuleInner}, - types::{FuncIndex, FuncSig, LocalOrImport, SigIndex, Type, Value}, - vm::{self, ImportBacking}, + backend::RunnableModule, + module::ModuleInfo, + typed_func::{Wasm, WasmTrapInfo}, + types::{LocalFuncIndex, SigIndex}, + vm, }; #[cfg(unix)] @@ -28,164 +27,89 @@ thread_local! { pub static TRAP_EARLY_DATA: Cell>> = Cell::new(None); } -pub struct Trapper; - -impl UserTrapper for Trapper { - unsafe fn do_early_trap(&self, data: Box) -> ! { - TRAP_EARLY_DATA.with(|cell| cell.set(Some(data))); - trigger_trap() - } +pub enum CallProtError { + Trap(WasmTrapInfo), + Error(Box), } pub struct Caller { - func_export_set: HashSet, handler_data: HandlerData, trampolines: Arc, + resolver: FuncResolver, } impl Caller { pub fn new( - module: &ModuleInfo, handler_data: HandlerData, trampolines: Arc, + resolver: FuncResolver, ) -> Self { - let mut func_export_set = HashSet::new(); - for export_index in module.exports.values() { - if let ExportIndex::Func(func_index) = export_index { - func_export_set.insert(*func_index); - } - } - if let Some(start_func_index) = module.start_func { - func_export_set.insert(start_func_index); - } - Self { - func_export_set, handler_data, trampolines, + resolver, } } } -impl ProtectedCaller for Caller { - 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); +impl RunnableModule for Caller { + fn get_func(&self, _: &ModuleInfo, func_index: LocalFuncIndex) -> Option> { + self.resolver.lookup(func_index) + } - let vmctx_ptr = match ctx { - Context::External(external_vmctx) => external_vmctx, - Context::Internal => vmctx, - }; + fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option { + unsafe extern "C" fn invoke( + trampoline: unsafe extern "C" fn(*mut vm::Ctx, NonNull, *const u64, *mut u64), + ctx: *mut vm::Ctx, + func: NonNull, + args: *const u64, + rets: *mut u64, + trap_info: *mut WasmTrapInfo, + user_error: *mut Option>, + invoke_env: Option>, + ) -> bool { + let handler_data = &*invoke_env.unwrap().cast().as_ptr(); - assert!(self.func_export_set.contains(&func_index)); + #[cfg(not(target_os = "windows"))] + let res = call_protected(handler_data, || { + // Leap of faith. + trampoline(ctx, func, args, rets); + }); - assert!( - signature.returns().len() <= 1, - "multi-value returns not yet supported" - ); + // the trampoline is called from C on windows + #[cfg(target_os = "windows")] + let res = call_protected(handler_data, trampoline, ctx, func, args, rets); - 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()]; + match res { + Err(err) => { + match err { + CallProtError::Trap(info) => *trap_info = info, + CallProtError::Error(data) => *user_error = Some(data), + } + false + } + Ok(()) => true, + } + } let trampoline = self .trampolines .lookup(sig_index) .expect("that trampoline doesn't exist"); - #[cfg(not(target_os = "windows"))] - call_protected(&self.handler_data, || unsafe { - // Leap of faith. - trampoline( - vmctx_ptr, - func_ptr, - param_vec.as_ptr(), - return_vec.as_mut_ptr(), - ); - })?; - - // 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()) - .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()) - } - - fn get_early_trapper(&self) -> Box { - Box::new(Trapper) - } -} - -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), + Some(unsafe { + Wasm::from_raw_parts( + trampoline, + invoke, + Some(NonNull::from(&self.handler_data).cast()), ) - } - }; + }) + } - let signature = &module.info.signatures[sig_index]; - - (func_ptr, ctx, signature, sig_index) + unsafe fn do_early_trap(&self, data: Box) -> ! { + TRAP_EARLY_DATA.with(|cell| cell.set(Some(data))); + trigger_trap() + } } unsafe impl Send for HandlerData {} diff --git a/lib/clif-backend/src/signal/unix.rs b/lib/clif-backend/src/signal/unix.rs index feaf4e2e2..a56613384 100644 --- a/lib/clif-backend/src/signal/unix.rs +++ b/lib/clif-backend/src/signal/unix.rs @@ -10,7 +10,7 @@ //! unless you have memory unsafety elsewhere in your code. //! use crate::relocation::{TrapCode, TrapData}; -use crate::signal::HandlerData; +use crate::signal::{CallProtError, HandlerData}; use libc::{c_int, c_void, siginfo_t}; use nix::sys::signal::{ sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV, @@ -18,7 +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}; +use wasmer_runtime_core::typed_func::WasmTrapInfo; extern "C" fn signal_trap_handler( signum: ::nix::libc::c_int, @@ -62,7 +62,10 @@ pub unsafe fn trigger_trap() -> ! { longjmp(jmp_buf as *mut c_void, 0) } -pub fn call_protected(handler_data: &HandlerData, f: impl FnOnce() -> T) -> RuntimeResult { +pub fn call_protected( + handler_data: &HandlerData, + f: impl FnOnce() -> T, +) -> Result { unsafe { let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get()); let prev_jmp_buf = *jmp_buf; @@ -76,7 +79,7 @@ pub fn call_protected(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R *jmp_buf = prev_jmp_buf; if let Some(data) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) { - Err(RuntimeError::Panic { data }) + Err(CallProtError::Error(data)) } else { let (faulting_addr, inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get()); @@ -85,33 +88,18 @@ pub fn call_protected(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R srcloc: _, }) = handler_data.lookup(inst_ptr) { - Err(match Signal::from_c_int(signum) { + Err(CallProtError::Trap(match Signal::from_c_int(signum) { Ok(SIGILL) => 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(), - }, - }, - Ok(SIGSEGV) | Ok(SIGBUS) => RuntimeError::Trap { - msg: "memory out-of-bounds access".into(), - }, - Ok(SIGFPE) => RuntimeError::Trap { - msg: "illegal arithmetic operation".into(), + TrapCode::BadSignature => WasmTrapInfo::IncorrectCallIndirectSignature, + TrapCode::IndirectCallToNull => WasmTrapInfo::CallIndirectOOB, + TrapCode::HeapOutOfBounds => WasmTrapInfo::MemoryOutOfBounds, + TrapCode::TableOutOfBounds => WasmTrapInfo::CallIndirectOOB, + _ => WasmTrapInfo::Unknown, }, + Ok(SIGSEGV) | Ok(SIGBUS) => WasmTrapInfo::MemoryOutOfBounds, + Ok(SIGFPE) => WasmTrapInfo::IllegalArithmetic, _ => unimplemented!(), - } - .into()) + })) } else { let signal = match Signal::from_c_int(signum) { Ok(SIGFPE) => "floating-point exception", @@ -119,13 +107,11 @@ pub fn call_protected(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R Ok(SIGSEGV) => "segmentation violation", Ok(SIGBUS) => "bus error", Err(_) => "error while getting the Signal", - _ => "unkown trapped signal", + _ => "unknown trapped signal", }; // When the trap-handler is fully implemented, this will return more information. - Err(RuntimeError::Trap { - msg: format!("unknown trap at {:p} - {}", faulting_addr, signal).into(), - } - .into()) + let s = format!("unknown trap at {:p} - {}", faulting_addr, signal); + Err(CallProtError::Error(Box::new(s))) } } } else { diff --git a/lib/clif-backend/src/signal/windows.rs b/lib/clif-backend/src/signal/windows.rs index e5bd41994..d755cd575 100644 --- a/lib/clif-backend/src/signal/windows.rs +++ b/lib/clif-backend/src/signal/windows.rs @@ -1,24 +1,24 @@ use crate::relocation::{TrapCode, TrapData}; -use crate::signal::HandlerData; +use crate::signal::{CallProtError, HandlerData}; use crate::trampoline::Trampoline; use std::cell::Cell; use std::ffi::c_void; -use std::ptr; +use std::ptr::{self, NonNull}; +use wasmer_runtime_core::typed_func::WasmTrapInfo; 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_ACCESS_VIOLATION, EXCEPTION_ARRAY_BOUNDS_EXCEEDED, EXCEPTION_BREAKPOINT, + EXCEPTION_DATATYPE_MISALIGNMENT, 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, + EXCEPTION_FLT_STACK_CHECK, EXCEPTION_FLT_UNDERFLOW, EXCEPTION_GUARD_PAGE, + EXCEPTION_ILLEGAL_INSTRUCTION, EXCEPTION_INT_DIVIDE_BY_ZERO, EXCEPTION_INT_OVERFLOW, + EXCEPTION_INVALID_HANDLE, EXCEPTION_IN_PAGE_ERROR, EXCEPTION_NONCONTINUABLE_EXCEPTION, + EXCEPTION_POSSIBLE_DEADLOCK, EXCEPTION_PRIV_INSTRUCTION, EXCEPTION_SINGLE_STEP, + EXCEPTION_STACK_OVERFLOW, }; thread_local! { @@ -29,10 +29,10 @@ pub fn call_protected( handler_data: &HandlerData, trampoline: Trampoline, ctx: *mut Ctx, - func: *const Func, + func: NonNull, param_vec: *const u64, return_vec: *mut u64, -) -> RuntimeResult<()> { +) -> Result<(), CallProtError> { // TODO: trap early // user code error // if let Some(msg) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) { @@ -46,9 +46,9 @@ pub fn call_protected( } let CallProtectedData { - code: signum, - exceptionAddress: exception_address, - instructionPointer: instruction_pointer, + code, + exception_address, + instruction_pointer, } = result.unwrap_err(); if let Some(TrapData { @@ -56,40 +56,24 @@ pub fn call_protected( srcloc: _, }) = handler_data.lookup(instruction_pointer as _) { - Err(match signum as DWORD { - EXCEPTION_ACCESS_VIOLATION => RuntimeError::Trap { - msg: "memory out-of-bounds access".into(), - }, + Err(CallProtError::Trap(match code as DWORD { + EXCEPTION_ACCESS_VIOLATION => WasmTrapInfo::MemoryOutOfBounds, 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(), - }, + TrapCode::BadSignature => WasmTrapInfo::IncorrectCallIndirectSignature, + TrapCode::IndirectCallToNull => WasmTrapInfo::CallIndirectOOB, + TrapCode::HeapOutOfBounds => WasmTrapInfo::MemoryOutOfBounds, + TrapCode::TableOutOfBounds => WasmTrapInfo::CallIndirectOOB, + TrapCode::UnreachableCodeReached => WasmTrapInfo::Unreachable, + _ => WasmTrapInfo::Unknown, }, - 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()) + EXCEPTION_STACK_OVERFLOW => WasmTrapInfo::Unknown, + EXCEPTION_INT_DIVIDE_BY_ZERO | EXCEPTION_INT_OVERFLOW => { + WasmTrapInfo::IllegalArithmetic + } + _ => WasmTrapInfo::Unknown, + })) } else { - let signal = match signum as DWORD { + let signal = match code as DWORD { EXCEPTION_FLT_DENORMAL_OPERAND | EXCEPTION_FLT_DIVIDE_BY_ZERO | EXCEPTION_FLT_INEXACT_RESULT @@ -99,13 +83,28 @@ pub fn call_protected( | EXCEPTION_FLT_UNDERFLOW => "floating-point exception", EXCEPTION_ILLEGAL_INSTRUCTION => "illegal instruction", EXCEPTION_ACCESS_VIOLATION => "segmentation violation", - _ => "unkown trapped signal", + EXCEPTION_DATATYPE_MISALIGNMENT => "datatype misalignment", + EXCEPTION_BREAKPOINT => "breakpoint", + EXCEPTION_SINGLE_STEP => "single step", + EXCEPTION_ARRAY_BOUNDS_EXCEEDED => "array bounds exceeded", + EXCEPTION_INT_DIVIDE_BY_ZERO => "int div by zero", + EXCEPTION_INT_OVERFLOW => "int overflow", + EXCEPTION_PRIV_INSTRUCTION => "priv instruction", + EXCEPTION_IN_PAGE_ERROR => "in page error", + EXCEPTION_NONCONTINUABLE_EXCEPTION => "non continuable exception", + EXCEPTION_STACK_OVERFLOW => "stack overflow", + EXCEPTION_GUARD_PAGE => "guard page", + EXCEPTION_INVALID_HANDLE => "invalid handle", + EXCEPTION_POSSIBLE_DEADLOCK => "possible deadlock", + _ => "unknown exception code", }; - Err(RuntimeError::Trap { - msg: format!("unknown trap at {} - {}", exception_address, signal).into(), - } - .into()) + let s = format!( + "unhandled trap at {:x} - code #{:x}: {}", + exception_address, code, signal, + ); + + Err(CallProtError::Error(Box::new(s))) } } diff --git a/lib/clif-backend/src/trampoline.rs b/lib/clif-backend/src/trampoline.rs index b20109ef0..09ee4cf74 100644 --- a/lib/clif-backend/src/trampoline.rs +++ b/lib/clif-backend/src/trampoline.rs @@ -6,8 +6,7 @@ use cranelift_codegen::{ isa, Context, }; use hashbrown::HashMap; -use std::ffi::c_void; -use std::{iter, mem}; +use std::{iter, mem, ptr::NonNull}; use wasmer_runtime_core::{ backend::sys::{Memory, Protect}, module::{ExportIndex, ModuleInfo}, @@ -23,8 +22,7 @@ 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 type Trampoline = unsafe extern "C" fn(*mut vm::Ctx, NonNull, *const u64, *mut u64); pub struct Trampolines { memory: Memory, diff --git a/lib/emscripten/Cargo.toml b/lib/emscripten/Cargo.toml index cd691e566..f89f96ca6 100644 --- a/lib/emscripten/Cargo.toml +++ b/lib/emscripten/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-emscripten" -version = "0.2.1" +version = "0.4.1" description = "Wasmer runtime emscripten implementation library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,25 +9,26 @@ edition = "2018" build = "build/mod.rs" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.2.1" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } lazy_static = "1.2.0" libc = "0.2.49" byteorder = "1" time = "0.1.41" +wasmer-clif-backend = { path = "../clif-backend", version = "0.4.1" } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.1", optional = true } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.4.1", optional = true } [target.'cfg(windows)'.dependencies] rand = "0.6" [dev-dependencies] -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 +llvm = ["wasmer-llvm-backend"] +singlepass = ["wasmer-singlepass-backend"] +debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"] diff --git a/lib/emscripten/build/emtests.rs b/lib/emscripten/build/emtests.rs index 09c4419bf..4a543683d 100644 --- a/lib/emscripten/build/emtests.rs +++ b/lib/emscripten/build/emtests.rs @@ -46,26 +46,42 @@ pub fn compile(file: &str, ignores: &Vec) -> Option { output_path.set_extension("js"); let output_str = output_path.to_str().unwrap(); - // Compile to wasm - let _wasm_compilation = Command::new("emcc") - .arg(file) - .arg("-s") - .arg("WASM=1") - .arg("-o") - .arg(output_str) - .output() - .expect("failed to execute process"); + let wasm_file_metadata = { + let mut wasm_file_path = PathBuf::from(file); + wasm_file_path.set_extension("wasm"); + if let Ok(wasm_file) = File::open(wasm_file_path) { + Some(wasm_file.metadata().unwrap()) + } else { + None + } + }; - // panic!("{:?}", wasm_compilation); - // if output.stderr { - // panic!("{}", output.stderr); - // } - // Remove js file + let real_file = File::open(file).unwrap(); + let file_metadata = real_file.metadata().unwrap(); + if wasm_file_metadata.is_none() + || file_metadata.modified().unwrap() >= wasm_file_metadata.unwrap().modified().unwrap() + { + // Compile to wasm + let _wasm_compilation = Command::new("emcc") + .arg(file) + .arg("-s") + .arg("WASM=1") + .arg("-o") + .arg(output_str) + .output() + .expect("failed to execute process"); - if Path::new(output_str).is_file() { - fs::remove_file(output_str).unwrap(); - } else { - println!("Output JS not found: {}", output_str); + // panic!("{:?}", wasm_compilation); + // if output.stderr { + // panic!("{}", output.stderr); + // } + // Remove js file + + if Path::new(output_str).is_file() { + fs::remove_file(output_str).unwrap(); + } else { + println!("Output JS not found: {}", output_str); + } } let mut output_path = PathBuf::from(file); diff --git a/lib/emscripten/emtests/hello.cpp b/lib/emscripten/emtests/hello.cpp new file mode 100644 index 000000000..fd90a65b3 --- /dev/null +++ b/lib/emscripten/emtests/hello.cpp @@ -0,0 +1,4 @@ +#include +int main() { + std::cout << "hello world\n"; +} diff --git a/lib/emscripten/emtests/hello.out b/lib/emscripten/emtests/hello.out new file mode 100644 index 000000000..9a71f81a4 --- /dev/null +++ b/lib/emscripten/emtests/hello.out @@ -0,0 +1,2 @@ +hello world + diff --git a/lib/emscripten/emtests/hello.wasm b/lib/emscripten/emtests/hello.wasm new file mode 100644 index 000000000..4e9541665 Binary files /dev/null and b/lib/emscripten/emtests/hello.wasm differ diff --git a/lib/emscripten/emtests/ignores.txt b/lib/emscripten/emtests/ignores.txt index 5033a51c3..16ff1a2a7 100644 --- a/lib/emscripten/emtests/ignores.txt +++ b/lib/emscripten/emtests/ignores.txt @@ -30,16 +30,7 @@ test_i64 test_i64_7z test_i64_varargs test_llvm_intrinsics -test_longjmp2 -test_longjmp3 -test_longjmp4 -test_longjmp test_longjmp_exc -test_longjmp_funcptr -test_longjmp_repeat -test_longjmp_stacked -test_longjmp_throw -test_longjmp_unwind test_lower_intrinsics test_main_thread_async_em_asm test_mainenv @@ -71,4 +62,47 @@ test_wprintf test_std_cout_new test_strptime_reentrant test_gmtime -test_time_c \ No newline at end of file +test_time_c +test_execvp +test_nl_types +test_phiundef +test_pipe +test_printf_2 +test_printf_more +test_regex +test_relocatable_void_function +test_rounding +test_set_align +test_sintvars +test_sizeof +test_sscanf +test_sscanf_3 +test_sscanf_4 +test_sscanf_5 +test_sscanf_6 +test_sscanf_caps +test_sscanf_float +test_sscanf_n +test_strcasecmp +test_strcmp_uni +test_strndup +test_strstr +test_strtod +test_strtok +test_strtol_bin +test_strtol_dec +test_strtol_hex +test_strtol_oct +test_strtoll_bin +test_strtoll_dec +test_strtoll_hex +test_strtoll_oct +test_struct_varargs +test_transtrcase +test_trickystring +test_unary_literal +test_vfs +test_vprintf +test_vsnprintf +test_write_stdout_fileno +test_zerodiv diff --git a/lib/emscripten/emtests/test_vfs.c b/lib/emscripten/emtests/test_vfs.c new file mode 100644 index 000000000..e7fc5c129 --- /dev/null +++ b/lib/emscripten/emtests/test_vfs.c @@ -0,0 +1,12 @@ +#include +#include +#include + +int main() { + char data[256] = {0}; + ssize_t fd = open("data.txt", 0); + ssize_t result = read((int)fd, &data, 255); + printf("content: %s", data); + printf("fd: %zd\n", fd); + return 0; +} diff --git a/lib/emscripten/emtests/test_vfs.md b/lib/emscripten/emtests/test_vfs.md new file mode 100644 index 000000000..e399c8138 --- /dev/null +++ b/lib/emscripten/emtests/test_vfs.md @@ -0,0 +1,6 @@ +The wasm file `test_vfs.wasm` is generated by compiling the `test_vfs.c` and writing a tar.zst blob with a single file +named `data.txt`. + +The program expects to find a file named `data.txt` and reads the contents and the file descriptor. + +The runtime should mount the virtual filesystem and expose the file. \ No newline at end of file diff --git a/lib/emscripten/emtests/test_vfs.out b/lib/emscripten/emtests/test_vfs.out new file mode 100644 index 000000000..5d8528aff --- /dev/null +++ b/lib/emscripten/emtests/test_vfs.out @@ -0,0 +1 @@ +content: wasmer is awesomer \ No newline at end of file diff --git a/lib/emscripten/emtests/test_vfs.wasm b/lib/emscripten/emtests/test_vfs.wasm new file mode 100644 index 000000000..d1f8e6f82 Binary files /dev/null and b/lib/emscripten/emtests/test_vfs.wasm differ diff --git a/lib/emscripten/emtests/test_vfs_bundle.wasm b/lib/emscripten/emtests/test_vfs_bundle.wasm new file mode 100644 index 000000000..92f3bbbf9 Binary files /dev/null and b/lib/emscripten/emtests/test_vfs_bundle.wasm differ diff --git a/lib/emscripten/emtests/test_vfs_data.txt b/lib/emscripten/emtests/test_vfs_data.txt new file mode 100644 index 000000000..00f2295a8 --- /dev/null +++ b/lib/emscripten/emtests/test_vfs_data.txt @@ -0,0 +1 @@ +wasmer is awesomer \ No newline at end of file diff --git a/lib/emscripten/src/bitwise.rs b/lib/emscripten/src/bitwise.rs new file mode 100644 index 000000000..6868c402a --- /dev/null +++ b/lib/emscripten/src/bitwise.rs @@ -0,0 +1,9 @@ +use crate::emscripten_target; +use wasmer_runtime_core::vm::Ctx; + +///emscripten: _llvm_bswap_i64 +pub fn _llvm_bswap_i64(_ctx: &mut Ctx, _low: i32, high: i32) -> i32 { + debug!("emscripten::_llvm_bswap_i64"); + emscripten_target::setTempRet0(_ctx, _low.swap_bytes()); + high.swap_bytes() +} diff --git a/lib/emscripten/src/emscripten_target.rs b/lib/emscripten/src/emscripten_target.rs index 4508f4f54..88073bc32 100644 --- a/lib/emscripten/src/emscripten_target.rs +++ b/lib/emscripten/src/emscripten_target.rs @@ -1,90 +1,29 @@ #![allow(non_snake_case)] use crate::env::get_emscripten_data; +#[cfg(target_os = "linux")] +use libc::getdtablesize; use wasmer_runtime_core::vm::Ctx; -pub fn setTempRet0(_ctx: &mut Ctx, _a: i32) { - debug!("emscripten::setTempRet0"); +pub fn setTempRet0(ctx: &mut Ctx, val: i32) { + debug!("emscripten::setTempRet0: {}", val); + get_emscripten_data(ctx).temp_ret_0 = val; } -pub fn getTempRet0(_ctx: &mut Ctx) -> i32 { + +pub fn getTempRet0(ctx: &mut Ctx) -> i32 { debug!("emscripten::getTempRet0"); + get_emscripten_data(ctx).temp_ret_0 +} + +pub fn _atexit(_ctx: &mut Ctx, _func: i32) -> i32 { + debug!("emscripten::_atexit"); + // TODO: implement atexit properly + // __ATEXIT__.unshift({ + // func: func, + // arg: arg + // }); 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 @@ -115,10 +54,69 @@ pub fn _dladdr(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { debug!("emscripten::_dladdr"); 0 } +pub fn _pthread_attr_init(_ctx: &mut Ctx, _a: i32) -> i32 { + debug!("emscripten::_pthread_attr_init({})", _a); + 0 +} +pub fn _pthread_attr_destroy(_ctx: &mut Ctx, _a: i32) -> i32 { + debug!("emscripten::_pthread_attr_destroy"); + 0 +} +pub fn _pthread_attr_getstack( + _ctx: &mut Ctx, + _stackaddr: i32, + _stacksize: i32, + _other: i32, +) -> i32 { + debug!( + "emscripten::_pthread_attr_getstack({}, {}, {})", + _stackaddr, _stacksize, _other + ); + // TODO: Translate from Emscripten + // HEAP32[stackaddr >> 2] = STACK_BASE; + // HEAP32[stacksize >> 2] = TOTAL_STACK; + 0 +} pub fn _pthread_cond_destroy(_ctx: &mut Ctx, _a: i32) -> i32 { debug!("emscripten::_pthread_cond_destroy"); 0 } +pub fn _pthread_cond_timedwait(_ctx: &mut Ctx, _a: i32, _b: i32, _c: i32) -> i32 { + debug!("emscripten::_pthread_cond_timedwait"); + 0 +} +pub fn _pthread_getspecific(_ctx: &mut Ctx, _a: i32) -> i32 { + debug!("emscripten::_pthread_getspecific"); + 0 +} +pub fn _pthread_getattr_np(_ctx: &mut Ctx, _thread: i32, _attr: i32) -> i32 { + debug!("emscripten::_pthread_getattr_np({}, {})", _thread, _attr); + 0 +} +pub fn _pthread_setspecific(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { + debug!("emscripten::_pthread_setspecific"); + 0 +} +pub fn _pthread_once(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { + debug!("emscripten::_pthread_once"); + 0 +} +pub fn _pthread_key_create(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { + debug!("emscripten::_pthread_key_create"); + 0 +} +pub fn _pthread_create(_ctx: &mut Ctx, _a: i32, _b: i32, _c: i32, _d: i32) -> i32 { + debug!("emscripten::_pthread_create"); + 0 +} +pub fn _pthread_detach(_ctx: &mut Ctx, _a: i32) -> i32 { + debug!("emscripten::_pthread_detach"); + 0 +} +pub fn _pthread_join(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { + debug!("emscripten::_pthread_join"); + 0 +} pub fn _pthread_cond_init(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { debug!("emscripten::_pthread_cond_init"); 0 @@ -171,6 +169,10 @@ pub fn _pthread_rwlock_unlock(_ctx: &mut Ctx, _a: i32) -> i32 { debug!("emscripten::_pthread_rwlock_unlock"); 0 } +pub fn _pthread_setcancelstate(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { + debug!("emscripten::_pthread_setcancelstate"); + 0 +} pub fn ___gxx_personality_v0( _ctx: &mut Ctx, _a: i32, @@ -183,93 +185,127 @@ pub fn ___gxx_personality_v0( debug!("emscripten::___gxx_personality_v0"); 0 } -// round 2 -pub fn nullFunc_dii(_ctx: &mut Ctx, _index: i32) { - debug!("emscripten::nullFunc_dii"); +#[cfg(target_os = "linux")] +pub fn _getdtablesize(_ctx: &mut Ctx) -> i32 { + debug!("emscripten::getdtablesize"); + unsafe { getdtablesize() } } -pub fn nullFunc_diiii(_ctx: &mut Ctx, _index: i32) { - debug!("emscripten::nullFunc_diiii"); +#[cfg(not(target_os = "linux"))] +pub fn _getdtablesize(_ctx: &mut Ctx) -> i32 { + debug!("emscripten::getdtablesize"); + -1 } -pub fn nullFunc_iiji(_ctx: &mut Ctx, _index: i32) { - debug!("emscripten::nullFunc_iiji"); +pub fn _gethostbyaddr(_ctx: &mut Ctx, _addr: i32, _addrlen: i32, _atype: i32) -> i32 { + debug!("emscripten::gethostbyaddr"); + 0 } -pub fn nullFunc_j(_ctx: &mut Ctx, _index: i32) { - debug!("emscripten::nullFunc_j"); +pub fn _gethostbyname_r( + _ctx: &mut Ctx, + _name: i32, + _ret: i32, + _buf: i32, + _buflen: i32, + _out: i32, + _err: i32, +) -> i32 { + debug!("emscripten::gethostbyname_r"); + 0 } -pub fn nullFunc_jij(_ctx: &mut Ctx, _index: i32) { - debug!("emscripten::nullFunc_jij"); +// NOTE: php.js has proper impl; libc has proper impl for linux +pub fn _getloadavg(_ctx: &mut Ctx, _loadavg: i32, _nelem: i32) -> i32 { + debug!("emscripten::getloadavg"); + 0 } -pub fn nullFunc_jjj(_ctx: &mut Ctx, _index: i32) { - debug!("emscripten::nullFunc_jjj"); + +// Invoke functions +// They save the stack to allow unwinding + +// Macro definitions +macro_rules! invoke { + ($ctx: ident, $name:ident, $( $arg:ident ),*) => {{ + let sp = get_emscripten_data($ctx).stack_save.as_ref().expect("stack_save is None").call().expect("stack_save call failed"); + let result = get_emscripten_data($ctx).$name.as_ref().expect(concat!("Dynamic call is None: ", stringify!($name))).call($($arg),*); + match result { + Ok(v) => v, + Err(_e) => { + get_emscripten_data($ctx).stack_restore.as_ref().expect("stack_restore is None").call(sp).expect("stack_restore call failed"); + // TODO: We should check if _e != "longjmp" and if that's the case, re-throw the error + // JS version is: if (e !== e+0 && e !== 'longjmp') throw e; + get_emscripten_data($ctx).set_threw.as_ref().expect("set_threw is None").call(1, 0).expect("set_threw call failed"); + 0 as _ + } + } + }}; } -pub fn nullFunc_vd(_ctx: &mut Ctx, _index: i32) { - debug!("emscripten::nullFunc_vd"); +macro_rules! invoke_no_return { + ($ctx: ident, $name:ident, $( $arg:ident ),*) => {{ + let sp = get_emscripten_data($ctx).stack_save.as_ref().expect("stack_save is None").call().expect("stack_save call failed"); + let result = get_emscripten_data($ctx).$name.as_ref().expect(concat!("Dynamic call is None: ", stringify!($name))).call($($arg),*); + match result { + Ok(v) => v, + Err(_e) => { + get_emscripten_data($ctx).stack_restore.as_ref().expect("stack_restore is None").call(sp).expect("stack_restore call failed"); + // TODO: We should check if _e != "longjmp" and if that's the case, re-throw the error + // JS version is: if (e !== e+0 && e !== 'longjmp') throw e; + get_emscripten_data($ctx).set_threw.as_ref().expect("set_threw is None").call(1, 0).expect("set_threw call failed"); + } + } + }}; } -pub fn nullFunc_viiiiiii(_ctx: &mut Ctx, _index: i32) { - debug!("emscripten::nullFunc_viiiiiii"); + +// Invoke functions +pub fn invoke_i(ctx: &mut Ctx, index: i32) -> i32 { + debug!("emscripten::invoke_i"); + invoke!(ctx, dyn_call_i, index) } -pub fn nullFunc_viiiiiiii(_ctx: &mut Ctx, _index: i32) { - debug!("emscripten::nullFunc_viiiiiiii"); +pub fn invoke_ii(ctx: &mut Ctx, index: i32, a1: i32) -> i32 { + debug!("emscripten::invoke_ii"); + invoke!(ctx, dyn_call_ii, index, a1) } -pub fn nullFunc_viiiiiiiii(_ctx: &mut Ctx, _index: i32) { - debug!("emscripten::nullFunc_viiiiiiiii"); +pub fn invoke_iii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) -> i32 { + debug!("emscripten::invoke_iii"); + invoke!(ctx, dyn_call_iii, index, a1, a2) } -pub fn nullFunc_viiij(_ctx: &mut Ctx, _index: i32) { - debug!("emscripten::nullFunc_viiij"); +pub fn invoke_iiii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) -> i32 { + debug!("emscripten::invoke_iiii"); + invoke!(ctx, dyn_call_iiii, index, a1, a2, a3) } -pub fn nullFunc_viiijiiii(_ctx: &mut Ctx, _index: i32) { - debug!("emscripten::nullFunc_viiijiiii"); +pub fn invoke_iifi(ctx: &mut Ctx, index: i32, a1: i32, a2: f64, a3: i32) -> i32 { + debug!("emscripten::invoke_iifi"); + invoke!(ctx, dyn_call_iifi, index, a1, a2, a3) } -pub fn nullFunc_viiijiiiiii(_ctx: &mut Ctx, _index: i32) { - debug!("emscripten::nullFunc_viiijiiiiii"); +pub fn invoke_v(ctx: &mut Ctx, index: i32) { + debug!("emscripten::invoke_v"); + invoke_no_return!(ctx, dyn_call_v, index); } -pub fn nullFunc_viij(_ctx: &mut Ctx, _index: i32) { - debug!("emscripten::nullFunc_viij"); +pub fn invoke_vi(ctx: &mut Ctx, index: i32, a1: i32) { + debug!("emscripten::invoke_vi"); + invoke_no_return!(ctx, dyn_call_vi, index, a1); } -pub fn nullFunc_viiji(_ctx: &mut Ctx, _index: i32) { - debug!("emscripten::nullFunc_viiji"); +pub fn invoke_vii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) { + debug!("emscripten::invoke_vii"); + invoke_no_return!(ctx, dyn_call_vii, index, a1, a2); } -pub fn nullFunc_viijiii(_ctx: &mut Ctx, _index: i32) { - debug!("emscripten::nullFunc_viijiii"); + +pub fn invoke_viii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) { + debug!("emscripten::invoke_viii"); + invoke_no_return!(ctx, dyn_call_viii, index, a1, a2, a3); } -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_viiii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32) { + debug!("emscripten::invoke_viiii"); + invoke_no_return!(ctx, dyn_call_viiii, index, a1, a2, a3, a4); } 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"); - } + invoke!(ctx, dyn_call_dii, index, a1, a2) } 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"); - } + invoke!(ctx, dyn_call_diiii, index, a1, a2, a3, a4) } 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"); - } + invoke!(ctx, dyn_call_iiiii, index, a1, a2, a3, a4) } pub fn invoke_iiiiii( ctx: &mut Ctx, @@ -281,27 +317,129 @@ pub fn invoke_iiiiii( 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"); - } + invoke!(ctx, dyn_call_iiiiii, index, a1, a2, a3, a4, a5) +} +pub fn invoke_iiiiiii( + ctx: &mut Ctx, + index: i32, + a1: i32, + a2: i32, + a3: i32, + a4: i32, + a5: i32, + a6: i32, +) -> i32 { + debug!("emscripten::invoke_iiiiiii"); + invoke!(ctx, dyn_call_iiiiiii, index, a1, a2, a3, a4, a5, a6) +} +pub fn invoke_iiiiiiii( + ctx: &mut Ctx, + index: i32, + a1: i32, + a2: i32, + a3: i32, + a4: i32, + a5: i32, + a6: i32, + a7: i32, +) -> i32 { + debug!("emscripten::invoke_iiiiiiii"); + invoke!(ctx, dyn_call_iiiiiiii, index, a1, a2, a3, a4, a5, a6, a7) +} +pub fn invoke_iiiiiiiii( + ctx: &mut Ctx, + index: i32, + a1: i32, + a2: i32, + a3: i32, + a4: i32, + a5: i32, + a6: i32, + a7: i32, + a8: i32, +) -> i32 { + debug!("emscripten::invoke_iiiiiiiii"); + invoke!( + ctx, + dyn_call_iiiiiiiii, + index, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8 + ) +} +pub fn invoke_iiiiiiiiii( + ctx: &mut Ctx, + index: i32, + a1: i32, + a2: i32, + a3: i32, + a4: i32, + a5: i32, + a6: i32, + a7: i32, + a8: i32, + a9: i32, +) -> i32 { + debug!("emscripten::invoke_iiiiiiiiii"); + invoke!( + ctx, + dyn_call_iiiiiiiiii, + index, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8, + a9 + ) +} +pub fn invoke_iiiiiiiiiii( + 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, +) -> i32 { + debug!("emscripten::invoke_iiiiiiiiiii"); + invoke!( + ctx, + dyn_call_iiiiiiiiiii, + index, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8, + a9, + a10 + ) } 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"); - } + invoke_no_return!(ctx, dyn_call_vd, index, a1) } 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"); - } + invoke_no_return!(ctx, dyn_call_viiiii, index, a1, a2, a3, a4, a5) } pub fn invoke_viiiiii( ctx: &mut Ctx, @@ -314,13 +452,7 @@ pub fn invoke_viiiiii( 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"); - } + invoke_no_return!(ctx, dyn_call_viiiiii, index, a1, a2, a3, a4, a5, a6) } pub fn invoke_viiiiiii( ctx: &mut Ctx, @@ -334,13 +466,7 @@ pub fn invoke_viiiiiii( 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"); - } + invoke_no_return!(ctx, dyn_call_viiiiiii, index, a1, a2, a3, a4, a5, a6, a7) } pub fn invoke_viiiiiiii( ctx: &mut Ctx, @@ -355,13 +481,19 @@ pub fn invoke_viiiiiiii( 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"); - } + invoke_no_return!( + ctx, + dyn_call_viiiiiiii, + index, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8 + ) } pub fn invoke_viiiiiiiii( ctx: &mut Ctx, @@ -377,21 +509,75 @@ pub fn invoke_viiiiiiiii( 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"); - } + invoke_no_return!( + ctx, + dyn_call_viiiiiiiii, + index, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8, + a9 + ) } +pub fn invoke_viiiiiiiiii( + 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, +) { + debug!("emscripten::invoke_viiiiiiiiii"); + invoke_no_return!( + ctx, + dyn_call_viiiiiiiiii, + index, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8, + a9, + a10 + ) +} + +pub fn invoke_iij(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) -> i32 { + debug!("emscripten::invoke_iij"); + invoke!(ctx, dyn_call_iij, index, a1, a2, a3) +} + 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"); - } + invoke!(ctx, dyn_call_iiji, index, a1, a2, a3, a4) +} + +pub fn invoke_iiijj( + ctx: &mut Ctx, + index: i32, + a1: i32, + a2: i32, + a3: i32, + a4: i32, + a5: i32, + a6: i32, +) -> i32 { + debug!("emscripten::invoke_iiijj"); + invoke!(ctx, dyn_call_iiijj, index, a1, a2, a3, a4, a5, a6) } pub fn invoke_j(ctx: &mut Ctx, index: i32) -> i32 { debug!("emscripten::invoke_j"); @@ -409,6 +595,15 @@ pub fn invoke_ji(ctx: &mut Ctx, index: i32, a1: i32) -> i32 { panic!("dyn_call_ji is set to None"); } } +pub fn invoke_jii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) -> i32 { + debug!("emscripten::invoke_jii"); + if let Some(dyn_call_jii) = &get_emscripten_data(ctx).dyn_call_jii { + dyn_call_jii.call(index, a1, a2).unwrap() + } else { + panic!("dyn_call_jii 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 { @@ -532,6 +727,18 @@ pub fn invoke_viijj( panic!("dyn_call_viijj is set to None"); } } +pub fn invoke_vj(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) { + debug!("emscripten::invoke_vj"); + if let Some(dyn_call_vj) = &get_emscripten_data(ctx).dyn_call_vj { + dyn_call_vj.call(index, a1, a2).unwrap(); + } else { + panic!("dyn_call_vj is set to None"); + } +} +pub fn invoke_vjji(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32, a5: i32) { + debug!("emscripten::invoke_vjji"); + invoke_no_return!(ctx, dyn_call_vjji, index, a1, a2, a3, a4, a5) +} 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 { @@ -573,3 +780,42 @@ pub fn invoke_vijj(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32 panic!("dyn_call_vijj is set to None"); } } +pub fn invoke_viid(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: f64) { + debug!("emscripten::invoke_viid"); + invoke_no_return!(ctx, dyn_call_viid, index, a1, a2, a3); +} +pub fn invoke_viidii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: f64, a4: i32, a5: i32) { + debug!("emscripten::invoke_viidii"); + invoke_no_return!(ctx, dyn_call_viidii, index, a1, a2, a3, a4, a5); +} +pub fn invoke_viidddddddd( + ctx: &mut Ctx, + index: i32, + a1: i32, + a2: i32, + a3: f64, + a4: f64, + a5: f64, + a6: f64, + a7: f64, + a8: f64, + a9: f64, + a10: f64, +) { + debug!("emscripten::invoke_viidddddddd"); + invoke_no_return!( + ctx, + dyn_call_viidddddddd, + index, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8, + a9, + a10 + ); +} diff --git a/lib/emscripten/src/env/mod.rs b/lib/emscripten/src/env/mod.rs index 662d4ba94..897fb38f0 100644 --- a/lib/emscripten/src/env/mod.rs +++ b/lib/emscripten/src/env/mod.rs @@ -47,27 +47,64 @@ pub fn _getpagesize(_ctx: &mut Ctx) -> u32 { 16384 } +pub fn _times(ctx: &mut Ctx, buffer: u32) -> u32 { + if buffer != 0 { + call_memset(ctx, buffer, 0, 16); + } + 0 +} + #[allow(clippy::cast_ptr_alignment)] 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 (mut pool_offset, env_ptr, mut pool_ptr) = unsafe { let (pool_offset, _pool_slice): (u32, &mut [u8]) = allocate_on_stack(ctx, TOTAL_ENV_SIZE as u32); let (env_offset, _env_slice): (u32, &mut [u8]) = 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; + let pool_ptr = emscripten_memory_pointer!(ctx.memory(0), pool_offset) as *mut u8; *env_ptr = pool_offset as i32; *environment = env_offset as i32; - // *env_ptr = 0; + (pool_offset, env_ptr, pool_ptr) }; - // unsafe { - // *env_ptr = 0; - // }; + + // *env_ptr = 0; + let default_vars = vec![ + ["USER", "web_user"], + ["LOGNAME", "web_user"], + ["PATH", "/"], + ["PWD", "/"], + ["HOME", "/home/web_user"], + ["LANG", "C.UTF-8"], + ["_", "thisProgram"], + ]; + let mut strings = vec![]; + let mut total_size = 0; + for [key, val] in &default_vars { + let line = key.to_string() + "=" + val; + total_size += line.len(); + strings.push(line); + } + if total_size as u32 > TOTAL_ENV_SIZE { + panic!("Environment size exceeded TOTAL_ENV_SIZE!"); + } + unsafe { + for (i, s) in strings.iter().enumerate() { + for (j, c) in s.chars().enumerate() { + debug_assert!(c < u8::max_value() as char); + *pool_ptr.add(j) = c as u8; + } + *env_ptr.add(i * 4) = pool_offset as i32; + pool_offset += s.len() as u32 + 1; + pool_ptr = pool_ptr.add(s.len() + 1); + } + *env_ptr.add(strings.len() * 4) = 0; + } } pub fn ___assert_fail(_ctx: &mut Ctx, _a: c_int, _b: c_int, _c: c_int, _d: c_int) { diff --git a/lib/emscripten/src/env/unix/mod.rs b/lib/emscripten/src/env/unix/mod.rs index cc7e01b03..7c846150f 100644 --- a/lib/emscripten/src/env/unix/mod.rs +++ b/lib/emscripten/src/env/unix/mod.rs @@ -66,6 +66,8 @@ pub fn _unsetenv(ctx: &mut Ctx, name: c_int) -> c_int { #[allow(clippy::cast_ptr_alignment)] pub fn _getpwnam(ctx: &mut Ctx, name_ptr: c_int) -> c_int { debug!("emscripten::_getpwnam {}", name_ptr); + #[cfg(feature = "debug")] + let _ = name_ptr; #[repr(C)] struct GuestPasswd { diff --git a/lib/emscripten/src/env/windows/mod.rs b/lib/emscripten/src/env/windows/mod.rs index e95a66ec2..f738eccbb 100644 --- a/lib/emscripten/src/env/windows/mod.rs +++ b/lib/emscripten/src/env/windows/mod.rs @@ -7,7 +7,6 @@ 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" { @@ -29,10 +28,8 @@ pub fn _getenv(ctx: &mut Ctx, name: u32) -> u32 { } /// emscripten: _setenv // (name: *const char, name: *const value, overwrite: int); -pub fn _setenv(ctx: &mut Ctx, name: u32, value: u32, overwrite: u32) -> 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); // setenv does not exist on windows, so we hack it with _putenv let name = read_string_from_wasm(ctx.memory(0), name); let value = read_string_from_wasm(ctx.memory(0), value); @@ -47,17 +44,16 @@ pub fn _setenv(ctx: &mut Ctx, name: u32, value: u32, overwrite: u32) -> c_int { /// emscripten: _putenv // (name: *const char); 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; - - debug!("=> name({:?})", unsafe { CStr::from_ptr(name_addr) }); + debug!("=> name({:?})", unsafe { + std::ffi::CStr::from_ptr(name_addr) + }); unsafe { putenv(name_addr) } } /// emscripten: _unsetenv // (name: *const char); 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); // no unsetenv on windows, so use putenv with an empty value let unsetenv_string = format!("{}=", name); @@ -70,6 +66,8 @@ pub fn _unsetenv(ctx: &mut Ctx, name: u32) -> c_int { #[allow(clippy::cast_ptr_alignment)] pub fn _getpwnam(ctx: &mut Ctx, name_ptr: c_int) -> c_int { debug!("emscripten::_getpwnam {}", name_ptr); + #[cfg(not(feature = "debug"))] + let _ = name_ptr; #[repr(C)] struct GuestPasswd { @@ -102,6 +100,8 @@ pub fn _getpwnam(ctx: &mut Ctx, name_ptr: c_int) -> c_int { #[allow(clippy::cast_ptr_alignment)] pub fn _getgrnam(ctx: &mut Ctx, name_ptr: c_int) -> c_int { debug!("emscripten::_getgrnam {}", name_ptr); + #[cfg(not(feature = "debug"))] + let _ = name_ptr; #[repr(C)] struct GuestGroup { @@ -126,6 +126,8 @@ pub fn _getgrnam(ctx: &mut Ctx, name_ptr: c_int) -> c_int { pub fn _sysconf(_ctx: &mut Ctx, name: c_int) -> c_long { debug!("emscripten::_sysconf {}", name); + #[cfg(not(feature = "debug"))] + let _ = name; // stub because sysconf is not valid on windows 0 } diff --git a/lib/emscripten/src/exception.rs b/lib/emscripten/src/exception.rs index b0aab78dd..f37c015e5 100644 --- a/lib/emscripten/src/exception.rs +++ b/lib/emscripten/src/exception.rs @@ -14,3 +14,23 @@ pub fn ___cxa_throw(ctx: &mut Ctx, _ptr: u32, _ty: u32, _destructor: u32) { debug!("emscripten::___cxa_throw"); _abort(ctx); } + +pub fn ___cxa_begin_catch(_ctx: &mut Ctx, _exception_object_ptr: u32) -> i32 { + debug!("emscripten::___cxa_begin_catch"); + -1 +} + +pub fn ___cxa_end_catch(_ctx: &mut Ctx) { + debug!("emscripten::___cxa_end_catch"); +} + +pub fn ___cxa_uncaught_exception(_ctx: &mut Ctx) -> i32 { + debug!("emscripten::___cxa_uncaught_exception"); + -1 +} + +pub fn ___cxa_pure_virtual(_ctx: &mut Ctx) { + debug!("emscripten::___cxa_pure_virtual"); + // ABORT = true + panic!("Pure virtual function called!"); +} diff --git a/lib/emscripten/src/exec.rs b/lib/emscripten/src/exec.rs index f47f09bf0..5751e3199 100644 --- a/lib/emscripten/src/exec.rs +++ b/lib/emscripten/src/exec.rs @@ -1,3 +1,4 @@ +use crate::varargs::VarArgs; use libc::execvp as libc_execvp; use std::cell::Cell; use std::ffi::CString; @@ -38,3 +39,15 @@ pub fn execvp(ctx: &mut Ctx, command_name_offset: u32, argv_offset: u32) -> i32 let args_pointer = argv.as_ptr(); unsafe { libc_execvp(command_pointer, args_pointer) } } + +/// execl +pub fn execl(_ctx: &mut Ctx, _path_ptr: i32, _arg0_ptr: i32, _varargs: VarArgs) -> i32 { + debug!("emscripten::execl"); + -1 +} + +/// execle +pub fn execle(_ctx: &mut Ctx, _path_ptr: i32, _arg0_ptr: i32, _varargs: VarArgs) -> i32 { + debug!("emscripten::execle"); + -1 +} diff --git a/lib/emscripten/src/io/mod.rs b/lib/emscripten/src/io/mod.rs index aea5e6fc2..6666cd5af 100644 --- a/lib/emscripten/src/io/mod.rs +++ b/lib/emscripten/src/io/mod.rs @@ -9,3 +9,55 @@ pub use self::unix::*; #[cfg(windows)] pub use self::windows::*; + +use wasmer_runtime_core::vm::Ctx; + +/// getprotobyname +pub fn getprotobyname(_ctx: &mut Ctx, _name_ptr: i32) -> i32 { + debug!("emscripten::getprotobyname"); + unimplemented!() +} + +/// getprotobynumber +pub fn getprotobynumber(_ctx: &mut Ctx, _one: i32) -> i32 { + debug!("emscripten::getprotobynumber"); + unimplemented!() +} + +/// sigdelset +pub fn sigdelset(ctx: &mut Ctx, set: i32, signum: i32) -> i32 { + debug!("emscripten::sigdelset"); + let memory = ctx.memory(0); + #[allow(clippy::cast_ptr_alignment)] + let ptr = emscripten_memory_pointer!(memory, set) as *mut i32; + + unsafe { *ptr = *ptr & !(1 << (signum - 1)) } + + 0 +} + +/// sigfillset +pub fn sigfillset(ctx: &mut Ctx, set: i32) -> i32 { + debug!("emscripten::sigfillset"); + let memory = ctx.memory(0); + #[allow(clippy::cast_ptr_alignment)] + let ptr = emscripten_memory_pointer!(memory, set) as *mut i32; + + unsafe { + *ptr = -1; + } + + 0 +} + +/// tzset +pub fn tzset(_ctx: &mut Ctx) { + debug!("emscripten::tzset - stub"); + //unimplemented!() +} + +/// strptime +pub fn strptime(_ctx: &mut Ctx, _one: i32, _two: i32, _three: i32) -> i32 { + debug!("emscripten::strptime"); + unimplemented!() +} diff --git a/lib/emscripten/src/io/unix.rs b/lib/emscripten/src/io/unix.rs index da3ddeff9..d120e754d 100644 --- a/lib/emscripten/src/io/unix.rs +++ b/lib/emscripten/src/io/unix.rs @@ -1,4 +1,4 @@ -use libc::printf as _printf; +use libc::{chroot as _chroot, printf as _printf}; use wasmer_runtime_core::vm::Ctx; @@ -15,3 +15,16 @@ pub fn printf(ctx: &mut Ctx, memory_offset: i32, extra: i32) -> i32 { _printf(addr, extra) } } + +/// chroot +pub fn chroot(ctx: &mut Ctx, name_ptr: i32) -> i32 { + debug!("emscripten::chroot"); + let name = emscripten_memory_pointer!(ctx.memory(0), name_ptr) as *const i8; + unsafe { _chroot(name) } +} + +/// getpwuid +pub fn getpwuid(_ctx: &mut Ctx, _uid: i32) -> i32 { + debug!("emscripten::getpwuid"); + 0 +} diff --git a/lib/emscripten/src/io/windows.rs b/lib/emscripten/src/io/windows.rs index 903d05900..a3c6f70aa 100644 --- a/lib/emscripten/src/io/windows.rs +++ b/lib/emscripten/src/io/windows.rs @@ -1,4 +1,3 @@ -use libc::{c_char, c_int}; use wasmer_runtime_core::vm::Ctx; // This may be problematic for msvc which uses inline functions for the printf family @@ -15,16 +14,33 @@ use wasmer_runtime_core::vm::Ctx; //} /// putchar -pub fn putchar(ctx: &mut Ctx, chr: i32) { +pub fn putchar(_ctx: &mut Ctx, chr: i32) { unsafe { libc::putchar(chr) }; } /// printf -pub fn printf(ctx: &mut Ctx, memory_offset: i32, extra: i32) -> i32 { +pub fn printf(_ctx: &mut Ctx, memory_offset: i32, extra: i32) -> i32 { debug!("emscripten::printf {}, {}", memory_offset, extra); + #[cfg(not(feature = "debug"))] + { + let _ = memory_offset; + let _ = extra; + } // unsafe { // let addr = emscripten_memory_pointer!(ctx.memory(0), memory_offset) as _; // _printf(addr, extra) // } -1 } + +/// chroot +pub fn chroot(_ctx: &mut Ctx, _name_ptr: i32) -> i32 { + debug!("emscripten::chroot"); + unimplemented!() +} + +/// getpwuid +pub fn getpwuid(_ctx: &mut Ctx, _uid: i32) -> i32 { + debug!("emscripten::getpwuid"); + unimplemented!() +} diff --git a/lib/emscripten/src/jmp.rs b/lib/emscripten/src/jmp.rs index c5b54f02b..1d9691bc1 100644 --- a/lib/emscripten/src/jmp.rs +++ b/lib/emscripten/src/jmp.rs @@ -1,43 +1,61 @@ use super::env::get_emscripten_data; -use libc::{c_int, c_void}; -use std::cell::UnsafeCell; +use super::process::abort_with_message; +use libc::c_int; +// use std::cell::UnsafeCell; use wasmer_runtime_core::vm::Ctx; /// setjmp -pub fn __setjmp(ctx: &mut Ctx, env_addr: u32) -> 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, - // we use the environment address to store the index relative to jumps - // so the address of the jump it's outside the wasm memory itself. - let jump_index = emscripten_memory_pointer!(ctx.memory(0), env_addr) as *mut i8; - // We create the jump buffer outside of the wasm memory - 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 3value of jumps - *jump_index = jumps.len() as _; - // We hold the reference of the jump buffer - jumps.push(jump_buf); - result - } + abort_with_message(ctx, "missing function: _setjmp"); + unreachable!() + // unsafe { + // // Rather than using the env as the holder of the jump buffer pointer, + // // we use the environment address to store the index relative to jumps + // // so the address of the jump it's outside the wasm memory itself. + // let jump_index = emscripten_memory_pointer!(ctx.memory(0), env_addr) as *mut i8; + // // We create the jump buffer outside of the wasm memory + // 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 3value of jumps + // *jump_index = jumps.len() as _; + // // We hold the reference of the jump buffer + // jumps.push(jump_buf); + // result + // } } /// longjmp #[allow(unreachable_code)] -pub fn __longjmp(ctx: &mut Ctx, env_addr: u32, val: c_int) { +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 - let jump_index = emscripten_memory_pointer!(ctx.memory(0), env_addr) as *mut i8; - let jumps = &mut get_emscripten_data(ctx).jumps; - // We get the real jump buffer from the jumps vector, using the retrieved index - let jump_buf = &jumps[*jump_index as usize]; - longjmp(jump_buf.get() as _, val) - }; + abort_with_message(ctx, "missing function: _longjmp"); + // unsafe { + // // We retrieve the jump index from the env address + // let jump_index = emscripten_memory_pointer!(ctx.memory(0), env_addr) as *mut i8; + // let jumps = &mut get_emscripten_data(ctx).jumps; + // // We get the real jump buffer from the jumps vector, using the retrieved index + // let jump_buf = &jumps[*jump_index as usize]; + // longjmp(jump_buf.get() as _, val) + // }; } -extern "C" { - fn setjmp(env: *mut c_void) -> c_int; - fn longjmp(env: *mut c_void, val: c_int) -> !; +/// _longjmp +// This function differs from the js implementation, it should return Result<(), &'static str> +pub fn _longjmp(ctx: &mut Ctx, env_addr: i32, val: c_int) -> Result<(), ()> { + let val = if val == 0 { 1 } else { val }; + get_emscripten_data(ctx) + .set_threw + .as_ref() + .expect("set_threw is None") + .call(env_addr, val) + .expect("set_threw failed to call"); + // TODO: return Err("longjmp") + Err(()) } + +// extern "C" { +// fn setjmp(env: *mut c_void) -> c_int; +// fn longjmp(env: *mut c_void, val: c_int) -> !; +// } diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs index 1cde93620..6b0340513 100644 --- a/lib/emscripten/src/lib.rs +++ b/lib/emscripten/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + #[macro_use] extern crate wasmer_runtime_core; @@ -27,6 +29,7 @@ mod file_descriptor; pub mod stdio; // EMSCRIPTEN APIS +mod bitwise; mod emscripten_target; mod env; mod errno; @@ -39,7 +42,6 @@ mod linking; mod lock; mod math; mod memory; -mod nullfunc; mod process; mod signal; mod storage; @@ -50,7 +52,7 @@ mod varargs; pub use self::storage::{align_memory, static_alloc}; pub use self::utils::{ - allocate_cstr_on_stack, allocate_on_stack, get_emscripten_memory_size, + allocate_cstr_on_stack, allocate_on_stack, get_emscripten_memory_size, get_emscripten_metadata, get_emscripten_table_size, is_emscripten_module, }; @@ -83,6 +85,7 @@ pub struct EmscriptenData<'a> { pub dyn_call_ii: Option>, pub dyn_call_iii: Option>, pub dyn_call_iiii: Option>, + pub dyn_call_iifi: Option>, pub dyn_call_v: Option>, pub dyn_call_vi: Option>, pub dyn_call_vii: Option>, @@ -94,15 +97,27 @@ pub struct EmscriptenData<'a> { pub dyn_call_diiii: Option>, pub dyn_call_iiiii: Option>, pub dyn_call_iiiiii: Option>, + pub dyn_call_iiiiiii: Option>, + pub dyn_call_iiiiiiii: Option>, + pub dyn_call_iiiiiiiii: Option>, + pub dyn_call_iiiiiiiiii: + Option>, + pub dyn_call_iiiiiiiiiii: + 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_viiiiiiiiii: + Option>, + pub dyn_call_iij: Option>, pub dyn_call_iiji: Option>, + pub dyn_call_iiijj: Option>, pub dyn_call_j: Option>, pub dyn_call_ji: Option>, + pub dyn_call_jii: Option>, pub dyn_call_jij: Option>, pub dyn_call_jjj: Option>, pub dyn_call_viiij: Option>, @@ -113,21 +128,28 @@ pub struct EmscriptenData<'a> { pub dyn_call_viiji: Option>, pub dyn_call_viijiii: Option>, pub dyn_call_viijj: Option>, + pub dyn_call_vj: Option>, + pub dyn_call_vjji: Option>, pub dyn_call_vij: Option>, pub dyn_call_viji: Option>, pub dyn_call_vijiii: Option>, pub dyn_call_vijj: Option>, + pub dyn_call_viid: Option>, + pub dyn_call_viidii: Option>, + pub dyn_call_viidddddddd: + Option>, + pub temp_ret_0: i32, + + pub stack_save: Option>, + pub stack_restore: Option>, + pub set_threw: Option>, } impl<'a> EmscriptenData<'a> { pub fn new(instance: &'a mut Instance) -> EmscriptenData<'a> { let malloc = instance.func("_malloc").unwrap(); let free = instance.func("_free").unwrap(); - let memalign = if let Ok(func) = instance.func("_memalign") { - Some(func) - } else { - None - }; + let memalign = instance.func("_memalign").ok(); let memset = instance.func("_memset").unwrap(); let stack_alloc = instance.func("stackAlloc").unwrap(); @@ -135,6 +157,7 @@ impl<'a> EmscriptenData<'a> { 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_iifi = instance.func("dynCall_iifi").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(); @@ -146,15 +169,24 @@ impl<'a> EmscriptenData<'a> { 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_iiiiiii = instance.func("dynCall_iiiiiii").ok(); + let dyn_call_iiiiiiii = instance.func("dynCall_iiiiiiii").ok(); + let dyn_call_iiiiiiiii = instance.func("dynCall_iiiiiiiii").ok(); + let dyn_call_iiiiiiiiii = instance.func("dynCall_iiiiiiiiii").ok(); + let dyn_call_iiiiiiiiiii = instance.func("dynCall_iiiiiiiiiii").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_viiiiiiiiii = instance.func("dynCall_viiiiiiiiii").ok(); + let dyn_call_iij = instance.func("dynCall_iij").ok(); let dyn_call_iiji = instance.func("dynCall_iiji").ok(); + let dyn_call_iiijj = instance.func("dynCall_iiijj").ok(); let dyn_call_j = instance.func("dynCall_j").ok(); let dyn_call_ji = instance.func("dynCall_ji").ok(); + let dyn_call_jii = instance.func("dynCall_jii").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(); @@ -164,10 +196,19 @@ impl<'a> EmscriptenData<'a> { 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_vj = instance.func("dynCall_vj").ok(); + let dyn_call_vjji = instance.func("dynCall_vjji").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(); + let dyn_call_viid = instance.func("dynCall_viid").ok(); + let dyn_call_viidii = instance.func("dynCall_viidii").ok(); + let dyn_call_viidddddddd = instance.func("dynCall_viidddddddd").ok(); + + let stack_save = instance.func("stackSave").ok(); + let stack_restore = instance.func("stackRestore").ok(); + let set_threw = instance.func("_setThrew").ok(); EmscriptenData { malloc, @@ -180,6 +221,7 @@ impl<'a> EmscriptenData<'a> { dyn_call_ii, dyn_call_iii, dyn_call_iiii, + dyn_call_iifi, dyn_call_v, dyn_call_vi, dyn_call_vii, @@ -191,15 +233,24 @@ impl<'a> EmscriptenData<'a> { dyn_call_diiii, dyn_call_iiiii, dyn_call_iiiiii, + dyn_call_iiiiiii, + dyn_call_iiiiiiii, + dyn_call_iiiiiiiii, + dyn_call_iiiiiiiiii, + dyn_call_iiiiiiiiiii, dyn_call_vd, dyn_call_viiiii, dyn_call_viiiiii, dyn_call_viiiiiii, dyn_call_viiiiiiii, dyn_call_viiiiiiiii, + dyn_call_viiiiiiiiii, + dyn_call_iij, dyn_call_iiji, + dyn_call_iiijj, dyn_call_j, dyn_call_ji, + dyn_call_jii, dyn_call_jij, dyn_call_jjj, dyn_call_viiij, @@ -209,10 +260,20 @@ impl<'a> EmscriptenData<'a> { dyn_call_viiji, dyn_call_viijiii, dyn_call_viijj, + dyn_call_vj, + dyn_call_vjji, dyn_call_vij, dyn_call_viji, dyn_call_vijiii, dyn_call_vijj, + dyn_call_viid, + dyn_call_viidii, + dyn_call_viidddddddd, + temp_ret_0: 0, + + stack_save, + stack_restore, + set_threw, } } } @@ -222,63 +283,76 @@ pub fn run_emscripten_instance( instance: &mut Instance, path: &str, args: Vec<&str>, + entrypoint: Option, ) -> CallResult<()> { let mut data = EmscriptenData::new(instance); let data_ptr = &mut data as *mut _ as *mut c_void; instance.context_mut().data = data_ptr; + // ATINIT + // (used by C++) + if let Ok(_func) = instance.dyn_func("globalCtors") { + instance.call("globalCtors", &[])?; + } + if let Ok(_func) = instance.dyn_func("___emscripten_environ_constructor") { instance.call("___emscripten_environ_constructor", &[])?; } // println!("running emscripten instance"); - let main_func = instance.dyn_func("_main")?; - let num_params = main_func.signature().params().len(); - let _result = match num_params { - 2 => { - 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 => { - instance.call("_main", &[])?; - } - _ => panic!( - "The emscripten main function has received an incorrect number of params {}", - num_params - ), - }; + if let Some(ep) = entrypoint { + debug!("Running entry point: {}", &ep); + let arg = unsafe { allocate_cstr_on_stack(instance.context_mut(), args[0]).0 }; + //let (argc, argv) = store_module_arguments(instance.context_mut(), args); + instance.call(&ep, &[Value::I32(arg as i32)])?; + } else { + let main_func = instance.dyn_func("_main")?; + let num_params = main_func.signature().params().len(); + let _result = match num_params { + 2 => { + let mut new_args = vec![path]; + new_args.extend(args); + let (argc, argv) = store_module_arguments(instance.context_mut(), new_args); + instance.call("_main", &[Value::I32(argc as i32), Value::I32(argv as i32)])?; + } + 0 => { + instance.call("_main", &[])?; + } + _ => panic!( + "The emscripten main function has received an incorrect number of params {}", + num_params + ), + }; + } - // TODO atinit and atexit for emscripten + // TODO atexit for emscripten // println!("{:?}", data); Ok(()) } -fn store_module_arguments(ctx: &mut Ctx, path: &str, args: Vec<&str>) -> (u32, u32) { +fn store_module_arguments(ctx: &mut Ctx, 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(ctx, path).0 }; - for (slot, arg) in args_slice[1..argc].iter_mut().zip(args.iter()) { + for (slot, arg) in args_slice[0..argc].iter_mut().zip(args.iter()) { *slot = unsafe { allocate_cstr_on_stack(ctx, &arg).0 }; } let (argv_offset, argv_slice): (_, &mut [u32]) = - unsafe { allocate_on_stack(ctx, ((argc + 1) * 4) as u32) }; + unsafe { allocate_on_stack(ctx, ((argc) * 4) as u32) }; assert!(!argv_slice.is_empty()); for (slot, arg) in argv_slice[0..argc].iter_mut().zip(args_slice.iter()) { *slot = *arg } argv_slice[argc] = 0; - (argc as u32, argv_offset) + (argc as u32 - 1, argv_offset) } pub fn emscripten_set_up_memory(memory: &Memory, globals: &EmscriptenGlobalsData) { let dynamictop_ptr = globals.dynamictop_ptr; - let stack_max = globals.stack_max; - - let dynamic_base = align_memory(stack_max); + let dynamic_base = globals.dynamic_base; memory.view::()[(dynamictop_ptr / 4) as usize].set(dynamic_base); } @@ -289,6 +363,7 @@ pub struct EmscriptenGlobalsData { stacktop: u32, stack_max: u32, dynamictop_ptr: u32, + dynamic_base: u32, memory_base: u32, table_base: u32, temp_double_ptr: u32, @@ -303,6 +378,7 @@ pub struct EmscriptenGlobals { pub table: Table, pub memory_min: Pages, pub memory_max: Option, + pub null_func_names: Vec, } impl EmscriptenGlobals { @@ -357,7 +433,14 @@ impl EmscriptenGlobals { let temp_double_ptr = static_top; static_top += 16; - let dynamictop_ptr = static_alloc(&mut static_top, 4); + let (dynamic_base, dynamictop_ptr) = + get_emscripten_metadata(&module).unwrap_or_else(|| { + let dynamictop_ptr = static_alloc(&mut static_top, 4); + ( + align_memory(align_memory(static_top) + TOTAL_STACK), + dynamictop_ptr, + ) + }); let stacktop = align_memory(static_top); let stack_max = stacktop + TOTAL_STACK; @@ -367,6 +450,7 @@ impl EmscriptenGlobals { stacktop, stack_max, dynamictop_ptr, + dynamic_base, memory_base, table_base, temp_double_ptr, @@ -376,12 +460,29 @@ impl EmscriptenGlobals { emscripten_set_up_memory(&memory, &data); + let mut null_func_names = vec![]; + for ( + _, + 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 namespace == "env" && name.starts_with("nullFunc_") { + null_func_names.push(name.to_string()) + } + } + Self { data, memory, table, memory_min, memory_max, + null_func_names, } } } @@ -393,299 +494,364 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject func!(crate::memory::abort_on_cannot_grow_memory).to_export() }; - imports! { - "env" => { - "memory" => Export::Memory(globals.memory.clone()), - "table" => Export::Table(globals.table.clone()), + let mut env_ns = namespace! { + "memory" => Export::Memory(globals.memory.clone()), + "table" => Export::Table(globals.table.clone()), - // Globals - "STACKTOP" => Global::new(Value::I32(globals.data.stacktop as i32)), - "STACK_MAX" => Global::new(Value::I32(globals.data.stack_max as i32)), - "DYNAMICTOP_PTR" => Global::new(Value::I32(globals.data.dynamictop_ptr as i32)), - "tableBase" => Global::new(Value::I32(globals.data.table_base as i32)), - "__table_base" => Global::new(Value::I32(globals.data.table_base as i32)), - "ABORT" => Global::new(Value::I32(globals.data.abort as i32)), - "memoryBase" => Global::new(Value::I32(globals.data.memory_base as i32)), - "__memory_base" => Global::new(Value::I32(globals.data.memory_base as i32)), - "tempDoublePtr" => Global::new(Value::I32(globals.data.temp_double_ptr as i32)), + // Globals + "STACKTOP" => Global::new(Value::I32(globals.data.stacktop as i32)), + "STACK_MAX" => Global::new(Value::I32(globals.data.stack_max as i32)), + "DYNAMICTOP_PTR" => Global::new(Value::I32(globals.data.dynamictop_ptr as i32)), + "tableBase" => Global::new(Value::I32(globals.data.table_base as i32)), + "__table_base" => Global::new(Value::I32(globals.data.table_base as i32)), + "ABORT" => Global::new(Value::I32(globals.data.abort as i32)), + "memoryBase" => Global::new(Value::I32(globals.data.memory_base as i32)), + "__memory_base" => Global::new(Value::I32(globals.data.memory_base as i32)), + "tempDoublePtr" => Global::new(Value::I32(globals.data.temp_double_ptr as i32)), - // IO - "printf" => func!(crate::io::printf), - "putchar" => func!(crate::io::putchar), - "___lock" => func!(crate::lock::___lock), - "___unlock" => func!(crate::lock::___unlock), - "___wait" => func!(crate::lock::___wait), + // IO + "printf" => func!(crate::io::printf), + "putchar" => func!(crate::io::putchar), + "___lock" => func!(crate::lock::___lock), + "___unlock" => func!(crate::lock::___unlock), + "___wait" => func!(crate::lock::___wait), + "_flock" => func!(crate::lock::_flock), + "_chroot" => func!(crate::io::chroot), + "_getprotobyname" => func!(crate::io::getprotobyname), + "_getprotobynumber" => func!(crate::io::getprotobynumber), + "_getpwuid" => func!(crate::io::getpwuid), + "_sigdelset" => func!(crate::io::sigdelset), + "_sigfillset" => func!(crate::io::sigfillset), + "_tzset" => func!(crate::io::tzset), + "_strptime" => func!(crate::io::strptime), - // exec - "_execvp" => func!(crate::exec::execvp), + // exec + "_execvp" => func!(crate::exec::execvp), + "_execl" => func!(crate::exec::execl), + "_execle" => func!(crate::exec::execle), - // exit - "__exit" => func!(crate::exit::exit), + // exit + "__exit" => func!(crate::exit::exit), - // Env - "___assert_fail" => func!(crate::env::___assert_fail), - "_getenv" => func!(crate::env::_getenv), - "_setenv" => func!(crate::env::_setenv), - "_putenv" => func!(crate::env::_putenv), - "_unsetenv" => func!(crate::env::_unsetenv), - "_getpwnam" => func!(crate::env::_getpwnam), - "_getgrnam" => func!(crate::env::_getgrnam), - "___buildEnvironment" => func!(crate::env::___build_environment), - "___setErrNo" => func!(crate::errno::___seterrno), - "_getpagesize" => func!(crate::env::_getpagesize), - "_sysconf" => func!(crate::env::_sysconf), - "_getaddrinfo" => func!(crate::env::_getaddrinfo), + // Env + "___assert_fail" => func!(crate::env::___assert_fail), + "_getenv" => func!(crate::env::_getenv), + "_setenv" => func!(crate::env::_setenv), + "_putenv" => func!(crate::env::_putenv), + "_unsetenv" => func!(crate::env::_unsetenv), + "_getpwnam" => func!(crate::env::_getpwnam), + "_getgrnam" => func!(crate::env::_getgrnam), + "___buildEnvironment" => func!(crate::env::___build_environment), + "___setErrNo" => func!(crate::errno::___seterrno), + "_getpagesize" => func!(crate::env::_getpagesize), + "_sysconf" => func!(crate::env::_sysconf), + "_getaddrinfo" => func!(crate::env::_getaddrinfo), + "_times" => func!(crate::env::_times), - // Null func - "nullFunc_i" => func!(crate::nullfunc::nullfunc_i), - "nullFunc_ii" => func!(crate::nullfunc::nullfunc_ii), - "nullFunc_iii" => func!(crate::nullfunc::nullfunc_iii), - "nullFunc_iiii" => func!(crate::nullfunc::nullfunc_iiii), - "nullFunc_iiiii" => func!(crate::nullfunc::nullfunc_iiiii), - "nullFunc_iiiiii" => func!(crate::nullfunc::nullfunc_iiiiii), - "nullFunc_v" => func!(crate::nullfunc::nullfunc_v), - "nullFunc_vi" => func!(crate::nullfunc::nullfunc_vi), - "nullFunc_vii" => func!(crate::nullfunc::nullfunc_vii), - "nullFunc_viii" => func!(crate::nullfunc::nullfunc_viii), - "nullFunc_viiii" => func!(crate::nullfunc::nullfunc_viiii), - "nullFunc_viiiii" => func!(crate::nullfunc::nullfunc_viiiii), - "nullFunc_viiiiii" => func!(crate::nullfunc::nullfunc_viiiiii), + // Syscalls + "___syscall1" => func!(crate::syscalls::___syscall1), + "___syscall3" => func!(crate::syscalls::___syscall3), + "___syscall4" => func!(crate::syscalls::___syscall4), + "___syscall5" => func!(crate::syscalls::___syscall5), + "___syscall6" => func!(crate::syscalls::___syscall6), + "___syscall9" => func!(crate::syscalls::___syscall9), + "___syscall10" => func!(crate::syscalls::___syscall10), + "___syscall12" => func!(crate::syscalls::___syscall12), + "___syscall15" => func!(crate::syscalls::___syscall15), + "___syscall20" => func!(crate::syscalls::___syscall20), + "___syscall33" => func!(crate::syscalls::___syscall33), + "___syscall34" => func!(crate::syscalls::___syscall34), + "___syscall39" => func!(crate::syscalls::___syscall39), + "___syscall38" => func!(crate::syscalls::___syscall38), + "___syscall40" => func!(crate::syscalls::___syscall40), + "___syscall41" => func!(crate::syscalls::___syscall41), + "___syscall42" => func!(crate::syscalls::___syscall42), + "___syscall54" => func!(crate::syscalls::___syscall54), + "___syscall57" => func!(crate::syscalls::___syscall57), + "___syscall60" => func!(crate::syscalls::___syscall60), + "___syscall63" => func!(crate::syscalls::___syscall63), + "___syscall64" => func!(crate::syscalls::___syscall64), + "___syscall66" => func!(crate::syscalls::___syscall66), + "___syscall75" => func!(crate::syscalls::___syscall75), + "___syscall77" => func!(crate::syscalls::___syscall77), + "___syscall83" => func!(crate::syscalls::___syscall83), + "___syscall85" => func!(crate::syscalls::___syscall85), + "___syscall91" => func!(crate::syscalls::___syscall91), + "___syscall94" => func!(crate::syscalls::___syscall94), + "___syscall97" => func!(crate::syscalls::___syscall97), + "___syscall102" => func!(crate::syscalls::___syscall102), + "___syscall110" => func!(crate::syscalls::___syscall110), + "___syscall114" => func!(crate::syscalls::___syscall114), + "___syscall118" => func!(crate::syscalls::___syscall118), + "___syscall122" => func!(crate::syscalls::___syscall122), + "___syscall140" => func!(crate::syscalls::___syscall140), + "___syscall142" => func!(crate::syscalls::___syscall142), + "___syscall145" => func!(crate::syscalls::___syscall145), + "___syscall146" => func!(crate::syscalls::___syscall146), + "___syscall148" => func!(crate::syscalls::___syscall148), + "___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), + "___syscall195" => func!(crate::syscalls::___syscall195), + "___syscall196" => func!(crate::syscalls::___syscall196), + "___syscall197" => func!(crate::syscalls::___syscall197), + "___syscall198" => func!(crate::syscalls::___syscall198), + "___syscall199" => func!(crate::syscalls::___syscall199), + "___syscall200" => func!(crate::syscalls::___syscall200), + "___syscall201" => func!(crate::syscalls::___syscall201), + "___syscall202" => func!(crate::syscalls::___syscall202), + "___syscall205" => func!(crate::syscalls::___syscall205), + "___syscall207" => func!(crate::syscalls::___syscall207), + "___syscall212" => func!(crate::syscalls::___syscall212), + "___syscall219" => func!(crate::syscalls::___syscall219), + "___syscall220" => func!(crate::syscalls::___syscall220), + "___syscall221" => func!(crate::syscalls::___syscall221), + "___syscall268" => func!(crate::syscalls::___syscall268), + "___syscall272" => func!(crate::syscalls::___syscall272), + "___syscall295" => func!(crate::syscalls::___syscall295), + "___syscall300" => func!(crate::syscalls::___syscall300), + "___syscall320" => func!(crate::syscalls::___syscall320), + "___syscall324" => func!(crate::syscalls::___syscall324), + "___syscall330" => func!(crate::syscalls::___syscall330), + "___syscall334" => func!(crate::syscalls::___syscall334), + "___syscall340" => func!(crate::syscalls::___syscall340), - // Syscalls - "___syscall1" => func!(crate::syscalls::___syscall1), - "___syscall3" => func!(crate::syscalls::___syscall3), - "___syscall4" => func!(crate::syscalls::___syscall4), - "___syscall5" => func!(crate::syscalls::___syscall5), - "___syscall6" => func!(crate::syscalls::___syscall6), - "___syscall10" => func!(crate::syscalls::___syscall10), - "___syscall12" => func!(crate::syscalls::___syscall12), - "___syscall15" => func!(crate::syscalls::___syscall15), - "___syscall20" => func!(crate::syscalls::___syscall20), - "___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), - "___syscall63" => func!(crate::syscalls::___syscall63), - "___syscall64" => func!(crate::syscalls::___syscall64), - "___syscall66" => func!(crate::syscalls::___syscall66), - "___syscall75" => func!(crate::syscalls::___syscall75), - "___syscall85" => func!(crate::syscalls::___syscall85), - "___syscall91" => func!(crate::syscalls::___syscall191), - "___syscall97" => func!(crate::syscalls::___syscall97), - "___syscall102" => func!(crate::syscalls::___syscall102), - "___syscall110" => func!(crate::syscalls::___syscall110), - "___syscall114" => func!(crate::syscalls::___syscall114), - "___syscall122" => func!(crate::syscalls::___syscall122), - "___syscall140" => func!(crate::syscalls::___syscall140), - "___syscall142" => func!(crate::syscalls::___syscall142), - "___syscall145" => func!(crate::syscalls::___syscall145), - "___syscall146" => func!(crate::syscalls::___syscall146), - "___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), - "___syscall195" => func!(crate::syscalls::___syscall195), - "___syscall196" => func!(crate::syscalls::___syscall196), - "___syscall197" => func!(crate::syscalls::___syscall197), - "___syscall199" => func!(crate::syscalls::___syscall199), - "___syscall201" => func!(crate::syscalls::___syscall201), - "___syscall202" => func!(crate::syscalls::___syscall202), - "___syscall212" => func!(crate::syscalls::___syscall212), - "___syscall220" => func!(crate::syscalls::___syscall220), - "___syscall221" => func!(crate::syscalls::___syscall221), - "___syscall268" => func!(crate::syscalls::___syscall268), - "___syscall272" => func!(crate::syscalls::___syscall272), - "___syscall295" => func!(crate::syscalls::___syscall295), - "___syscall300" => func!(crate::syscalls::___syscall300), - "___syscall330" => func!(crate::syscalls::___syscall330), - "___syscall334" => func!(crate::syscalls::___syscall334), - "___syscall340" => func!(crate::syscalls::___syscall340), - - // Process - "abort" => func!(crate::process::em_abort), - "_abort" => func!(crate::process::_abort), - "abortStackOverflow" => func!(crate::process::abort_stack_overflow), - "_llvm_trap" => func!(crate::process::_llvm_trap), - "_fork" => func!(crate::process::_fork), - "_exit" => func!(crate::process::_exit), - "_system" => func!(crate::process::_system), - "_popen" => func!(crate::process::_popen), - "_endgrent" => func!(crate::process::_endgrent), - "_execve" => func!(crate::process::_execve), - "_kill" => func!(crate::process::_kill), - "_llvm_stackrestore" => func!(crate::process::_llvm_stackrestore), - "_llvm_stacksave" => func!(crate::process::_llvm_stacksave), - "_raise" => func!(crate::process::_raise), - "_sem_init" => func!(crate::process::_sem_init), - "_sem_post" => func!(crate::process::_sem_post), - "_sem_wait" => func!(crate::process::_sem_wait), - "_getgrent" => func!(crate::process::_getgrent), - "_sched_yield" => func!(crate::process::_sched_yield), - "_setgrent" => func!(crate::process::_setgrent), - "_setgroups" => func!(crate::process::_setgroups), - "_setitimer" => func!(crate::process::_setitimer), - "_usleep" => func!(crate::process::_usleep), - "_utimes" => func!(crate::process::_utimes), - "_waitpid" => func!(crate::process::_waitpid), + // Process + "abort" => func!(crate::process::em_abort), + "_abort" => func!(crate::process::_abort), + "abortStackOverflow" => func!(crate::process::abort_stack_overflow), + "_llvm_trap" => func!(crate::process::_llvm_trap), + "_fork" => func!(crate::process::_fork), + "_exit" => func!(crate::process::_exit), + "_system" => func!(crate::process::_system), + "_popen" => func!(crate::process::_popen), + "_endgrent" => func!(crate::process::_endgrent), + "_execve" => func!(crate::process::_execve), + "_kill" => func!(crate::process::_kill), + "_llvm_stackrestore" => func!(crate::process::_llvm_stackrestore), + "_llvm_stacksave" => func!(crate::process::_llvm_stacksave), + "_llvm_eh_typeid_for" => func!(crate::process::_llvm_eh_typeid_for), + "_raise" => func!(crate::process::_raise), + "_sem_init" => func!(crate::process::_sem_init), + "_sem_destroy" => func!(crate::process::_sem_destroy), + "_sem_post" => func!(crate::process::_sem_post), + "_sem_wait" => func!(crate::process::_sem_wait), + "_getgrent" => func!(crate::process::_getgrent), + "_sched_yield" => func!(crate::process::_sched_yield), + "_setgrent" => func!(crate::process::_setgrent), + "_setgroups" => func!(crate::process::_setgroups), + "_setitimer" => func!(crate::process::_setitimer), + "_usleep" => func!(crate::process::_usleep), + "_nanosleep" => func!(crate::process::_nanosleep), + "_utimes" => func!(crate::process::_utimes), + "_waitpid" => func!(crate::process::_waitpid), - // Signal - "_sigemptyset" => func!(crate::signal::_sigemptyset), - "_sigaddset" => func!(crate::signal::_sigaddset), - "_sigprocmask" => func!(crate::signal::_sigprocmask), - "_sigaction" => func!(crate::signal::_sigaction), - "_signal" => func!(crate::signal::_signal), - "_sigsuspend" => func!(crate::signal::_sigsuspend), + // Signal + "_sigemptyset" => func!(crate::signal::_sigemptyset), + "_sigaddset" => func!(crate::signal::_sigaddset), + "_sigprocmask" => func!(crate::signal::_sigprocmask), + "_sigaction" => func!(crate::signal::_sigaction), + "_signal" => func!(crate::signal::_signal), + "_sigsuspend" => func!(crate::signal::_sigsuspend), - // 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), + // 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), + "segfault" => func!(crate::memory::segfault), + "alignfault" => func!(crate::memory::alignfault), + "ftfault" => func!(crate::memory::ftfault), + "getTotalMemory" => func!(crate::memory::get_total_memory), + "___map_file" => func!(crate::memory::___map_file), - // Exception - "___cxa_allocate_exception" => func!(crate::exception::___cxa_allocate_exception), - "___cxa_throw" => func!(crate::exception::___cxa_throw), + // Exception + "___cxa_allocate_exception" => func!(crate::exception::___cxa_allocate_exception), + "___cxa_throw" => func!(crate::exception::___cxa_throw), + "___cxa_begin_catch" => func!(crate::exception::___cxa_begin_catch), + "___cxa_end_catch" => func!(crate::exception::___cxa_end_catch), + "___cxa_uncaught_exception" => func!(crate::exception::___cxa_uncaught_exception), + "___cxa_pure_virtual" => func!(crate::exception::___cxa_pure_virtual), - // Time - "_gettimeofday" => func!(crate::time::_gettimeofday), - "_clock_gettime" => func!(crate::time::_clock_gettime), - "___clock_gettime" => func!(crate::time::_clock_gettime), - "_clock" => func!(crate::time::_clock), - "_difftime" => func!(crate::time::_difftime), - "_asctime" => func!(crate::time::_asctime), - "_asctime_r" => func!(crate::time::_asctime_r), - "_localtime" => func!(crate::time::_localtime), - "_time" => func!(crate::time::_time), - "_strftime" => func!(crate::time::_strftime), - "_localtime_r" => func!(crate::time::_localtime_r), - "_gmtime_r" => func!(crate::time::_gmtime_r), - "_mktime" => func!(crate::time::_mktime), - "_gmtime" => func!(crate::time::_gmtime), + // Time + "_gettimeofday" => func!(crate::time::_gettimeofday), + "_clock_gettime" => func!(crate::time::_clock_gettime), + "___clock_gettime" => func!(crate::time::_clock_gettime), + "_clock" => func!(crate::time::_clock), + "_difftime" => func!(crate::time::_difftime), + "_asctime" => func!(crate::time::_asctime), + "_asctime_r" => func!(crate::time::_asctime_r), + "_localtime" => func!(crate::time::_localtime), + "_time" => func!(crate::time::_time), + "_strftime" => func!(crate::time::_strftime), + "_strftime_l" => func!(crate::time::_strftime_l), + "_localtime_r" => func!(crate::time::_localtime_r), + "_gmtime_r" => func!(crate::time::_gmtime_r), + "_mktime" => func!(crate::time::_mktime), + "_gmtime" => func!(crate::time::_gmtime), - // Math - "f64-rem" => func!(crate::math::f64_rem), - "_llvm_log10_f64" => func!(crate::math::_llvm_log10_f64), - "_llvm_log2_f64" => func!(crate::math::_llvm_log2_f64), - "_llvm_log10_f32" => func!(crate::math::_llvm_log10_f32), - "_llvm_log2_f32" => func!(crate::math::_llvm_log2_f64), - "_emscripten_random" => func!(crate::math::_emscripten_random), + // Math + "f64-rem" => func!(crate::math::f64_rem), + "_llvm_log10_f64" => func!(crate::math::_llvm_log10_f64), + "_llvm_log2_f64" => func!(crate::math::_llvm_log2_f64), + "_llvm_log10_f32" => func!(crate::math::_llvm_log10_f32), + "_llvm_log2_f32" => func!(crate::math::_llvm_log2_f64), + "_llvm_sin_f64" => func!(crate::math::_llvm_sin_f64), + "_llvm_cos_f64" => func!(crate::math::_llvm_cos_f64), + "_llvm_exp2_f32" => func!(crate::math::_llvm_exp2_f32), + "_llvm_exp2_f64" => func!(crate::math::_llvm_exp2_f64), + "_llvm_trunc_f64" => func!(crate::math::_llvm_trunc_f64), + "_emscripten_random" => func!(crate::math::_emscripten_random), - // Jump - "__setjmp" => func!(crate::jmp::__setjmp), - "__longjmp" => func!(crate::jmp::__longjmp), + // Jump + "__setjmp" => func!(crate::jmp::__setjmp), + "__longjmp" => func!(crate::jmp::__longjmp), + "_longjmp" => func!(crate::jmp::_longjmp), + "_emscripten_longjmp" => func!(crate::jmp::_longjmp), - // Linking - "_dlclose" => func!(crate::linking::_dlclose), - "_dlerror" => func!(crate::linking::_dlerror), - "_dlopen" => func!(crate::linking::_dlopen), - "_dlsym" => func!(crate::linking::_dlsym), + // Bitwise + "_llvm_bswap_i64" => func!(crate::bitwise::_llvm_bswap_i64), - // 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), - }, + // Linking + "_dlclose" => func!(crate::linking::_dlclose), + "_dlerror" => func!(crate::linking::_dlerror), + "_dlopen" => func!(crate::linking::_dlopen), + "_dlsym" => func!(crate::linking::_dlsym), + + // wasm32-unknown-emscripten + "_atexit" => func!(crate::emscripten_target::_atexit), + "setTempRet0" => func!(crate::emscripten_target::setTempRet0), + "getTempRet0" => func!(crate::emscripten_target::getTempRet0), + "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_iifi" => func!(crate::emscripten_target::invoke_iifi), + "invoke_v" => func!(crate::emscripten_target::invoke_v), + "invoke_vi" => func!(crate::emscripten_target::invoke_vi), + "invoke_vj" => func!(crate::emscripten_target::invoke_vj), + "invoke_vjji" => func!(crate::emscripten_target::invoke_vjji), + "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_create" => func!(crate::emscripten_target::_pthread_create), + "_pthread_detach" => func!(crate::emscripten_target::_pthread_detach), + "_pthread_join" => func!(crate::emscripten_target::_pthread_join), + "_pthread_attr_init" => func!(crate::emscripten_target::_pthread_attr_init), + "_pthread_attr_destroy" => func!(crate::emscripten_target::_pthread_attr_destroy), + "_pthread_attr_getstack" => func!(crate::emscripten_target::_pthread_attr_getstack), + "_pthread_cond_init" => func!(crate::emscripten_target::_pthread_cond_init), + "_pthread_cond_destroy" => func!(crate::emscripten_target::_pthread_cond_destroy), + "_pthread_cond_signal" => func!(crate::emscripten_target::_pthread_cond_signal), + "_pthread_cond_timedwait" => func!(crate::emscripten_target::_pthread_cond_timedwait), + "_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), + "_pthread_setcancelstate" => func!(crate::emscripten_target::_pthread_setcancelstate), + "_pthread_getspecific" => func!(crate::emscripten_target::_pthread_getspecific), + "_pthread_getattr_np" => func!(crate::emscripten_target::_pthread_getattr_np), + "_pthread_setspecific" => func!(crate::emscripten_target::_pthread_setspecific), + "_pthread_once" => func!(crate::emscripten_target::_pthread_once), + "_pthread_key_create" => func!(crate::emscripten_target::_pthread_key_create), + "___gxx_personality_v0" => func!(crate::emscripten_target::___gxx_personality_v0), + "_getdtablesize" => func!(crate::emscripten_target::_getdtablesize), + "_gethostbyaddr" => func!(crate::emscripten_target::_gethostbyaddr), + "_gethostbyname_r" => func!(crate::emscripten_target::_gethostbyname_r), + "_getloadavg" => func!(crate::emscripten_target::_getloadavg), + "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_iiiiiii" => func!(crate::emscripten_target::invoke_iiiiiii), + "invoke_iiiiiiii" => func!(crate::emscripten_target::invoke_iiiiiiii), + "invoke_iiiiiiiii" => func!(crate::emscripten_target::invoke_iiiiiiiii), + "invoke_iiiiiiiiii" => func!(crate::emscripten_target::invoke_iiiiiiiiii), + "invoke_iiiiiiiiiii" => func!(crate::emscripten_target::invoke_iiiiiiiiiii), + "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_viiiiiiiii" => func!(crate::emscripten_target::invoke_viiiiiiiii), + "invoke_viiiiiiiiii" => func!(crate::emscripten_target::invoke_viiiiiiiiii), + "invoke_iij" => func!(crate::emscripten_target::invoke_iij), + "invoke_iiji" => func!(crate::emscripten_target::invoke_iiji), + "invoke_iiijj" => func!(crate::emscripten_target::invoke_iiijj), + "invoke_j" => func!(crate::emscripten_target::invoke_j), + "invoke_ji" => func!(crate::emscripten_target::invoke_ji), + "invoke_jii" => func!(crate::emscripten_target::invoke_jii), + "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), + "invoke_viid" => func!(crate::emscripten_target::invoke_viid), + "invoke_viidii" => func!(crate::emscripten_target::invoke_viidii), + "invoke_viidddddddd" => func!(crate::emscripten_target::invoke_viidddddddd), + }; + + for null_func_name in globals.null_func_names.iter() { + env_ns.insert(null_func_name.as_str(), Func::new(nullfunc).to_export()); + } + + let import_object: ImportObject = imports! { + "env" => env_ns, "global" => { "NaN" => Global::new(Value::F64(f64::NAN)), "Infinity" => Global::new(Value::F64(f64::INFINITY)), }, "global.Math" => { "pow" => func!(crate::math::pow), + "exp" => func!(crate::math::exp), + "log" => func!(crate::math::log), }, "asm2wasm" => { "f64-rem" => func!(crate::math::f64_rem), + "f64-to-int" => func!(crate::math::f64_to_int), }, - } + }; + + import_object +} + +pub fn nullfunc(ctx: &mut Ctx, _x: u32) { + use crate::process::abort_with_message; + debug!("emscripten::nullfunc_i {}", _x); + abort_with_message(ctx, "Invalid function pointer. 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)"); } /// The current version of this crate diff --git a/lib/emscripten/src/lock.rs b/lib/emscripten/src/lock.rs index badb47a1b..f6804e9d1 100644 --- a/lib/emscripten/src/lock.rs +++ b/lib/emscripten/src/lock.rs @@ -15,3 +15,8 @@ pub fn ___unlock(_ctx: &mut Ctx, _what: c_int) { pub fn ___wait(_ctx: &mut Ctx, _which: u32, _varargs: u32, _three: u32, _four: u32) { debug!("emscripten::___wait"); } + +pub fn _flock(_ctx: &mut Ctx, _fd: u32, _op: u32) -> u32 { + debug!("emscripten::_flock"); + 0 +} diff --git a/lib/emscripten/src/math.rs b/lib/emscripten/src/math.rs index a60966a1c..3256b49c5 100644 --- a/lib/emscripten/src/math.rs +++ b/lib/emscripten/src/math.rs @@ -12,6 +12,18 @@ pub fn _llvm_log2_f64(_ctx: &mut Ctx, value: f64) -> f64 { value.log2() } +/// emscripten: _llvm_sin_f64 +pub fn _llvm_sin_f64(_ctx: &mut Ctx, value: f64) -> f64 { + debug!("emscripten::_llvm_sin_f64"); + value.sin() +} + +/// emscripten: _llvm_cos_f64 +pub fn _llvm_cos_f64(_ctx: &mut Ctx, value: f64) -> f64 { + debug!("emscripten::_llvm_cos_f64"); + value.cos() +} + pub fn _llvm_log10_f32(_ctx: &mut Ctx, _value: f64) -> f64 { debug!("emscripten::_llvm_log10_f32"); -1.0 @@ -22,12 +34,27 @@ pub fn _llvm_log2_f32(_ctx: &mut Ctx, _value: f64) -> f64 { -1.0 } +pub fn _llvm_exp2_f32(_ctx: &mut Ctx, value: f32) -> f32 { + debug!("emscripten::_llvm_exp2_f32"); + 2f32.powf(value) +} + +pub fn _llvm_exp2_f64(_ctx: &mut Ctx, value: f64) -> f64 { + debug!("emscripten::_llvm_exp2_f64"); + 2f64.powf(value) +} + +pub fn _llvm_trunc_f64(_ctx: &mut Ctx, value: f64) -> f64 { + debug!("emscripten::_llvm_trunc_f64"); + value.trunc() +} + pub fn _emscripten_random(_ctx: &mut Ctx) -> f64 { debug!("emscripten::_emscripten_random"); -1.0 } -// emscripten: f64-rem +// emscripten: asm2wasm.f64-rem pub fn f64_rem(_ctx: &mut Ctx, x: f64, y: f64) -> f64 { debug!("emscripten::f64-rem"); x % y @@ -37,3 +64,19 @@ pub fn f64_rem(_ctx: &mut Ctx, x: f64, y: f64) -> f64 { pub fn pow(_ctx: &mut Ctx, x: f64, y: f64) -> f64 { x.powf(y) } + +// emscripten: global.Math exp +pub fn exp(_ctx: &mut Ctx, value: f64) -> f64 { + value.exp() +} + +// emscripten: global.Math log +pub fn log(_ctx: &mut Ctx, value: f64) -> f64 { + value.ln() +} + +// emscripten: asm2wasm.f64-to-int +pub fn f64_to_int(_ctx: &mut Ctx, value: f64) -> i32 { + debug!("emscripten::f64_to_int {}", value); + value as i32 +} diff --git a/lib/emscripten/src/memory.rs b/lib/emscripten/src/memory.rs index 02877a011..0f1cb1f19 100644 --- a/lib/emscripten/src/memory.rs +++ b/lib/emscripten/src/memory.rs @@ -1,6 +1,9 @@ use super::process::abort_with_message; use libc::{c_int, c_void, memcpy, size_t}; -use wasmer_runtime_core::vm::Ctx; +use wasmer_runtime_core::{ + units::{Pages, WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE}, + vm::Ctx, +}; /// emscripten: _emscripten_memcpy_big pub fn _emscripten_memcpy_big(ctx: &mut Ctx, dest: u32, src: u32, len: u32) -> u32 { @@ -17,17 +20,49 @@ pub fn _emscripten_memcpy_big(ctx: &mut Ctx, dest: u32, src: u32, len: u32) -> u } /// 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 +pub fn _emscripten_get_heap_size(ctx: &mut Ctx) -> u32 { + debug!("emscripten::_emscripten_get_heap_size"); + let result = ctx.memory(0).size().bytes().0 as u32; + debug!("=> {}", result); + + result +} + +// From emscripten implementation +fn align_up(mut val: usize, multiple: usize) -> usize { + if val % multiple > 0 { + val += multiple - val % multiple; + } + val } /// 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 +/// Note: this function only allows growing the size of heap +pub fn _emscripten_resize_heap(ctx: &mut Ctx, requested_size: u32) -> u32 { + debug!("emscripten::_emscripten_resize_heap {}", requested_size); + let current_memory_pages = ctx.memory(0).size(); + let current_memory = current_memory_pages.bytes().0 as u32; + + // implementation from emscripten + let mut new_size = usize::max(current_memory as usize, WASM_MIN_PAGES * WASM_PAGE_SIZE); + while new_size < requested_size as usize { + if new_size <= 0x2000_0000 { + new_size = align_up(new_size * 2, WASM_PAGE_SIZE); + } else { + new_size = usize::min( + align_up((3 * new_size + 0x8000_0000) / 4, WASM_PAGE_SIZE), + WASM_PAGE_SIZE * WASM_MAX_PAGES, + ); + } + } + + let amount_to_grow = (new_size - current_memory as usize) / WASM_PAGE_SIZE; + if let Ok(_pages_allocated) = ctx.memory(0).grow(Pages(amount_to_grow as u32)) { + debug!("{} pages allocated", _pages_allocated.0); + 1 + } else { + 0 + } } /// emscripten: getTotalMemory @@ -35,7 +70,7 @@ pub fn get_total_memory(_ctx: &mut Ctx) -> u32 { debug!("emscripten::get_total_memory"); // instance.memories[0].current_pages() // TODO: Fix implementation - 16_777_216 + _ctx.memory(0).size().bytes().0 as u32 } /// emscripten: enlargeMemory @@ -63,6 +98,24 @@ pub fn abort_on_cannot_grow_memory_old(ctx: &mut Ctx) -> u32 { 0 } +/// emscripten: segfault +pub fn segfault(ctx: &mut Ctx) { + debug!("emscripten::segfault"); + abort_with_message(ctx, "segmentation fault"); +} + +/// emscripten: alignfault +pub fn alignfault(ctx: &mut Ctx) { + debug!("emscripten::alignfault"); + abort_with_message(ctx, "alignment fault"); +} + +/// emscripten: ftfault +pub fn ftfault(ctx: &mut Ctx) { + debug!("emscripten::ftfault"); + abort_with_message(ctx, "Function table mask error"); +} + /// emscripten: ___map_file pub fn ___map_file(_ctx: &mut Ctx, _one: u32, _two: u32) -> c_int { debug!("emscripten::___map_file"); diff --git a/lib/emscripten/src/nullfunc.rs b/lib/emscripten/src/nullfunc.rs deleted file mode 100644 index 8f289599b..000000000 --- a/lib/emscripten/src/nullfunc.rs +++ /dev/null @@ -1,67 +0,0 @@ -use super::process::abort_with_message; -use wasmer_runtime_core::vm::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(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(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(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(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(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(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(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(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(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(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(ctx: &mut Ctx, _x: u32) { - debug!("emscripten::nullfunc_viiiii"); - 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(ctx: &mut Ctx, _x: u32) { - debug!("emscripten::nullfunc_viiiiii"); - 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 2dbf90ca5..538ed528f 100644 --- a/lib/emscripten/src/process.rs +++ b/lib/emscripten/src/process.rs @@ -1,10 +1,9 @@ use libc::{abort, c_char, c_int, exit, EAGAIN}; #[cfg(not(target_os = "windows"))] -use libc::pid_t; - +type PidT = libc::pid_t; #[cfg(target_os = "windows")] -type pid_t = c_int; +type PidT = c_int; use std::ffi::CStr; use wasmer_runtime_core::vm::Ctx; @@ -22,7 +21,7 @@ pub fn _abort(_ctx: &mut Ctx) { } } -pub fn _fork(_ctx: &mut Ctx) -> pid_t { +pub fn _fork(_ctx: &mut Ctx) -> PidT { debug!("emscripten::_fork"); // unsafe { // fork() @@ -83,8 +82,13 @@ pub fn _raise(_ctx: &mut Ctx, _one: i32) -> i32 { } pub fn _sem_init(_ctx: &mut Ctx, _one: i32, _two: i32, _three: i32) -> i32 { - debug!("emscripten::_sem_init"); - -1 + debug!("emscripten::_sem_init: {}, {}, {}", _one, _two, _three); + 0 +} + +pub fn _sem_destroy(_ctx: &mut Ctx, _one: i32) -> i32 { + debug!("emscripten::_sem_destroy"); + 0 } pub fn _sem_post(_ctx: &mut Ctx, _one: i32) -> i32 { @@ -122,6 +126,11 @@ pub fn _usleep(_ctx: &mut Ctx, _one: i32) -> i32 { -1 } +pub fn _nanosleep(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { + debug!("emscripten::_nanosleep"); + -1 +} + pub fn _utimes(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::_utimes"); -1 @@ -146,11 +155,16 @@ pub fn _llvm_trap(ctx: &mut Ctx) { abort_with_message(ctx, "abort!"); } +pub fn _llvm_eh_typeid_for(_ctx: &mut Ctx, _type_info_addr: u32) -> i32 { + debug!("emscripten::_llvm_eh_typeid_for"); + -1 +} + 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; + EAGAIN } pub fn _popen(_ctx: &mut Ctx, _one: i32, _two: i32) -> c_int { diff --git a/lib/emscripten/src/stdio.rs b/lib/emscripten/src/stdio.rs index d7d8ab736..2b95bafc7 100644 --- a/lib/emscripten/src/stdio.rs +++ b/lib/emscripten/src/stdio.rs @@ -15,7 +15,7 @@ pub struct StdioCapturer { use libc::{STDERR_FILENO, STDOUT_FILENO}; #[cfg(target_os = "windows")] -const STDIN_FILENO: libc::c_int = 0; +const _STDIN_FILENO: libc::c_int = 0; #[cfg(target_os = "windows")] const STDOUT_FILENO: libc::c_int = 1; #[cfg(target_os = "windows")] diff --git a/lib/emscripten/src/syscalls/mod.rs b/lib/emscripten/src/syscalls/mod.rs index 806ea8187..5654a1299 100644 --- a/lib/emscripten/src/syscalls/mod.rs +++ b/lib/emscripten/src/syscalls/mod.rs @@ -28,19 +28,24 @@ use libc::{ getpid, // iovec, lseek, + off_t, // open, read, + rename, + // sockaddr_in, // readv, rmdir, // writev, stat, write, - // sockaddr_in, }; use wasmer_runtime_core::vm::Ctx; use super::env; use std::cell::Cell; +#[allow(unused_imports)] +use std::io::Error; +use std::mem; use std::slice; /// exit @@ -114,9 +119,21 @@ pub fn ___syscall20(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { unsafe { getpid() } } -pub fn ___syscall38(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { - debug!("emscripten::___syscall38"); - -1 +// rename +pub fn ___syscall38(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 { + debug!("emscripten::___syscall38 (rename)"); + let old_path_addr: u32 = varargs.get(ctx); + let new_path_addr: u32 = varargs.get(ctx); + let old_path = emscripten_memory_pointer!(ctx.memory(0), old_path_addr) as *const i8; + let new_path = emscripten_memory_pointer!(ctx.memory(0), new_path_addr) as *const i8; + let result = unsafe { rename(old_path, new_path) }; + debug!( + "=> old_path: {}, new_path: {}, result: {}", + unsafe { std::ffi::CStr::from_ptr(old_path).to_str().unwrap() }, + unsafe { std::ffi::CStr::from_ptr(new_path).to_str().unwrap() }, + result + ); + result } // rmdir @@ -190,8 +207,8 @@ pub fn ___syscall85(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { } pub fn ___syscall91(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { - debug!("emscripten::___syscall91"); - -1 + debug!("emscripten::___syscall91 - stub"); + 0 } pub fn ___syscall97(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { @@ -205,9 +222,11 @@ pub fn ___syscall110(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { } // getcwd -pub fn ___syscall183(ctx: &mut Ctx, buf_offset: u32, _size: u32) -> u32 { +pub fn ___syscall183(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 { debug!("emscripten::___syscall183"); use std::env; + let buf_offset: c_int = varargs.get(ctx); + let _size: c_int = varargs.get(ctx); let path = env::current_dir(); let path_string = path.unwrap().display().to_string(); let len = path_string.len(); @@ -240,12 +259,21 @@ pub fn ___syscall192(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in if fd == -1 { let ptr = env::call_memalign(ctx, 16384, len); if ptr == 0 { - return -1; + // ENOMEM + return -12; } + let real_ptr = emscripten_memory_pointer!(ctx.memory(0), ptr) as *const u8; env::call_memset(ctx, ptr, 0, len); - ptr as _ + for i in 0..(len as usize) { + unsafe { + assert_eq!(*real_ptr.add(i), 0); + } + } + debug!("=> ptr: {}", ptr); + return ptr as i32; } else { - -1 + // return ENODEV + return -19; } } @@ -254,10 +282,28 @@ pub fn ___syscall140(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 { // -> c_int debug!("emscripten::___syscall140 (lseek) {}", _which); let fd: i32 = varargs.get(ctx); - let offset: i32 = varargs.get(ctx); + let _ = varargs.get::(ctx); // ignore high offset + let offset_low: i32 = varargs.get(ctx); + let result_ptr_value = varargs.get::(ctx); let whence: i32 = varargs.get(ctx); - debug!("=> fd: {}, offset: {}, whence = {}", fd, offset, whence); - unsafe { lseek(fd, offset as _, whence) as _ } + let offset = offset_low as off_t; + let ret = unsafe { lseek(fd, offset, whence) as i32 }; + #[allow(clippy::cast_ptr_alignment)] + let result_ptr = emscripten_memory_pointer!(ctx.memory(0), result_ptr_value) as *mut i32; + assert_eq!(8, mem::align_of_val(&result_ptr)); + unsafe { + *result_ptr = ret; + } + debug!( + "=> fd: {}, offset: {}, result_ptr: {}, whence: {} = {}\nlast os error: {}", + fd, + offset, + result_ptr_value, + whence, + 0, + Error::last_os_error(), + ); + 0 } /// readv @@ -343,16 +389,6 @@ pub fn ___syscall191(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { -1 } -pub fn ___syscall194(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { - debug!("emscripten::___syscall194 - stub"); - -1 -} - -pub fn ___syscall196(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { - debug!("emscripten::___syscall194 - stub"); - -1 -} - pub fn ___syscall199(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall199 - stub"); -1 @@ -369,7 +405,14 @@ pub fn ___syscall195(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in unsafe { let mut _stat: stat = std::mem::zeroed(); let ret = stat(pathname_addr, &mut _stat); - debug!("ret: {}", ret); + debug!( + "=> pathname: {}, buf: {}, path: {} = {}\nlast os error: {}", + pathname, + buf, + std::ffi::CStr::from_ptr(pathname_addr).to_str().unwrap(), + ret, + Error::last_os_error() + ); if ret != 0 { return ret; } @@ -408,8 +451,14 @@ pub fn ___syscall221(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in // fcntl64 let _fd: i32 = varargs.get(ctx); let cmd: u32 = varargs.get(ctx); + // (FAPPEND - 0x08 + // |FASYNC - 0x40 + // |FFSYNC - 0x80 + // |FNONBLOCK - 0x04 + debug!("=> fd: {}, cmd: {}", _fd, cmd); match cmd { 2 => 0, + 13 | 14 => 0, // pretend file locking worked _ => -1, } } @@ -434,6 +483,12 @@ pub fn ___syscall300(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { -1 } +// utimensat +pub fn ___syscall320(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall320 (utimensat), {}", _which); + 0 +} + pub fn ___syscall334(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall334"); -1 diff --git a/lib/emscripten/src/syscalls/unix.rs b/lib/emscripten/src/syscalls/unix.rs index 1102b7a59..ec8b98f3f 100644 --- a/lib/emscripten/src/syscalls/unix.rs +++ b/lib/emscripten/src/syscalls/unix.rs @@ -1,29 +1,44 @@ use crate::varargs::VarArgs; +#[cfg(target_os = "macos")] +use libc::size_t; /// NOTE: TODO: These syscalls only support wasm_32 for now because they assume offsets are u32 /// Syscall list: https://www.cs.utexas.edu/~bismith/test/syscalls/syscalls32.html use libc::{ accept, + access, bind, - // ENOTTY, c_char, c_int, c_void, chown, // fcntl, setsockopt, getppid connect, + dup, dup2, + fchmod, + fchown, fcntl, + // ENOTTY, + fsync, getgid, + getgroups, getpeername, + getrusage, getsockname, getsockopt, + gid_t, in_addr_t, in_port_t, ioctl, + lchown, + link, // iovec, listen, mkdir, + mode_t, msghdr, + nice, + off_t, open, pid_t, pread, @@ -43,6 +58,9 @@ use libc::{ sockaddr, socket, socklen_t, + stat, + symlink, + uid_t, uname, utsname, EINVAL, @@ -57,6 +75,9 @@ use libc::{ }; use wasmer_runtime_core::vm::Ctx; +use crate::utils; +#[allow(unused_imports)] +use std::io::Error; use std::mem; // Linking to functions that are not provided by rust libc @@ -64,10 +85,13 @@ use std::mem; #[link(name = "c")] extern "C" { pub fn wait4(pid: pid_t, status: *mut c_int, options: c_int, rusage: *mut rusage) -> pid_t; + pub fn madvise(addr: *mut c_void, len: size_t, advice: c_int) -> c_int; + pub fn fdatasync(fd: c_int) -> c_int; + pub fn lstat64(path: *const c_char, buf: *mut c_void) -> c_int; } #[cfg(not(target_os = "macos"))] -use libc::wait4; +use libc::{fallocate, fdatasync, ftruncate64, lstat, madvise, wait4}; // Another conditional constant for name resolution: Macos et iOS use // SO_NOSIGPIPE as a setsockopt flag to disable SIGPIPE emission on socket. @@ -87,12 +111,113 @@ pub fn ___syscall5(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int 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 + "=> pathname: {}, flags: {}, mode: {} = fd: {}\npath: {}\nlast os error: {}", + pathname, + flags, + mode, + fd, + _path_str, + Error::last_os_error(), ); fd } +/// link +pub fn ___syscall9(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall9 (link) {}", _which); + + let oldname: c_int = varargs.get(ctx); + let newname: c_int = varargs.get(ctx); + let oldname_ptr = emscripten_memory_pointer!(ctx.memory(0), oldname) as *const i8; + let newname_ptr = emscripten_memory_pointer!(ctx.memory(0), newname) as *const i8; + let result = unsafe { link(oldname_ptr, newname_ptr) }; + debug!( + "=> oldname: {}, newname: {}, result: {}", + unsafe { std::ffi::CStr::from_ptr(oldname_ptr).to_str().unwrap() }, + unsafe { std::ffi::CStr::from_ptr(newname_ptr).to_str().unwrap() }, + result, + ); + result +} + +/// getrusage +pub fn ___syscall77(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall77 (getrusage) {}", _which); + + let resource: c_int = varargs.get(ctx); + let rusage_ptr: c_int = varargs.get(ctx); + #[allow(clippy::cast_ptr_alignment)] + let rusage = emscripten_memory_pointer!(ctx.memory(0), rusage_ptr) as *mut rusage; + assert_eq!(8, mem::align_of_val(&rusage)); + unsafe { getrusage(resource, rusage) } +} + +/// symlink +pub fn ___syscall83(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall83 (symlink) {}", _which); + + let path1_ptr: c_int = varargs.get(ctx); + let path2_ptr: c_int = varargs.get(ctx); + let path1 = emscripten_memory_pointer!(ctx.memory(0), path1_ptr) as *mut i8; + let path2 = emscripten_memory_pointer!(ctx.memory(0), path2_ptr) as *mut i8; + let result = unsafe { symlink(path1, path2) }; + debug!( + "=> path1: {}, path2: {}, result: {}", + unsafe { std::ffi::CStr::from_ptr(path1).to_str().unwrap() }, + unsafe { std::ffi::CStr::from_ptr(path2).to_str().unwrap() }, + result, + ); + result +} + +/// ftruncate64 +pub fn ___syscall194(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall194 (ftruncate64) {}", _which); + let _fd: c_int = varargs.get(ctx); + let _length: i64 = varargs.get(ctx); + #[cfg(not(target_os = "macos"))] + unsafe { + ftruncate64(_fd, _length) + } + #[cfg(target_os = "macos")] + unimplemented!() +} + +/// lchown +pub fn ___syscall198(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall198 (lchown) {}", _which); + let path: c_int = varargs.get(ctx); + let uid: uid_t = varargs.get(ctx); + let gid: gid_t = varargs.get(ctx); + let path_ptr = emscripten_memory_pointer!(ctx.memory(0), path) as *const i8; + let result = unsafe { lchown(path_ptr, uid, gid) }; + debug!( + "=> path: {}, uid: {}, gid: {}, result: {}", + unsafe { std::ffi::CStr::from_ptr(path_ptr).to_str().unwrap() }, + uid, + gid, + result, + ); + result +} + +/// getgroups +pub fn ___syscall205(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall205 (getgroups) {}", _which); + let ngroups_max: c_int = varargs.get(ctx); + let groups: c_int = varargs.get(ctx); + + #[allow(clippy::cast_ptr_alignment)] + let gid_ptr = emscripten_memory_pointer!(ctx.memory(0), groups) as *mut gid_t; + assert_eq!(4, mem::align_of_val(&gid_ptr)); + let result = unsafe { getgroups(ngroups_max, gid_ptr) }; + debug!( + "=> ngroups_max: {}, gid_ptr: {:?}, result: {}", + ngroups_max, gid_ptr, result, + ); + result +} + // chown pub fn ___syscall212(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall212 (chown) {}", _which); @@ -106,6 +231,42 @@ pub fn ___syscall212(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in unsafe { chown(pathname_addr, owner, group) } } +/// madvise +pub fn ___syscall219(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall212 (chown) {}", _which); + + let addr_ptr: c_int = varargs.get(ctx); + let len: usize = varargs.get(ctx); + let advice: c_int = varargs.get(ctx); + + let addr = emscripten_memory_pointer!(ctx.memory(0), addr_ptr) as *mut c_void; + + unsafe { madvise(addr, len, advice) } +} + +/// access +pub fn ___syscall33(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall33 (access) {}", _which); + let path_ptr: c_int = varargs.get(ctx); + let amode: c_int = varargs.get(ctx); + let path = emscripten_memory_pointer!(ctx.memory(0), path_ptr) as *const i8; + let result = unsafe { access(path, amode) }; + debug!( + "=> path: {}, amode: {}, result: {}", + unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }, + amode, + result + ); + result +} + +/// nice +pub fn ___syscall34(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall34 (nice) {}", _which); + let inc_r: c_int = varargs.get(ctx); + unsafe { nice(inc_r) } +} + // mkdir pub fn ___syscall39(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall39 (mkdir) {}", _which); @@ -115,6 +276,19 @@ pub fn ___syscall39(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int unsafe { mkdir(pathname_addr, mode as _) } } +/// dup +pub fn ___syscall41(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall41 (dup) {}", _which); + let fd: c_int = varargs.get(ctx); + unsafe { dup(fd) } +} + +/// getgid +pub fn ___syscall200(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { + debug!("emscripten::___syscall200 (getgid)"); + unsafe { getgid() as i32 } +} + // getgid pub fn ___syscall201(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall201 (getgid)"); @@ -134,6 +308,15 @@ pub fn ___syscall202(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { } } +/// fchown +pub fn ___syscall207(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall207 (fchown) {}", _which); + let fd: c_int = varargs.get(ctx); + let owner: uid_t = varargs.get(ctx); + let group: gid_t = varargs.get(ctx); + unsafe { fchown(fd, owner, group) } +} + /// dup3 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 @@ -173,8 +356,13 @@ 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); + debug!("=> fd: {}, op: {}", fd, request); // Got the equivalents here: https://code.woboq.org/linux/linux/include/uapi/asm-generic/ioctls.h.html + // let argp: u32 = varargs.get(ctx); + // let argp_ptr = emscripten_memory_pointer!(ctx.memory(0), argp) as *mut c_void; + // let ret = unsafe { ioctl(fd, request as _, argp_ptr) }; + // debug!("=> {}", ret); + // ret match request as _ { 21537 => { // FIONBIO @@ -282,7 +470,7 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in 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 + unsafe { (*_proper_address).sin_family }, unsafe { (*_proper_address).sin_port }, unsafe { (*_proper_address).sin_addr.s_addr } ); let status = unsafe { bind(socket, address, address_len) }; @@ -499,6 +687,14 @@ pub fn ___syscall181(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in status } +/// fchmod +pub fn ___syscall94(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall118 (fchmod) {}", _which); + let fd: c_int = varargs.get(ctx); + let mode: mode_t = varargs.get(ctx); + unsafe { fchmod(fd, mode) } +} + /// wait4 #[allow(clippy::cast_ptr_alignment)] pub fn ___syscall114(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> pid_t { @@ -508,6 +704,7 @@ pub fn ___syscall114(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> pid_ let options: c_int = varargs.get(ctx); let rusage: u32 = varargs.get(ctx); let status_addr = emscripten_memory_pointer!(ctx.memory(0), status) as *mut c_int; + let rusage_addr = emscripten_memory_pointer!(ctx.memory(0), rusage) as *mut rusage; let res = unsafe { wait4(pid, status_addr, options, rusage_addr) }; debug!( @@ -517,6 +714,13 @@ pub fn ___syscall114(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> pid_ res } +/// fsync +pub fn ___syscall118(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall118 (fsync) {}", _which); + let fd: c_int = varargs.get(ctx); + unsafe { fsync(fd) } +} + // select #[allow(clippy::cast_ptr_alignment)] pub fn ___syscall142(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { @@ -537,6 +741,15 @@ pub fn ___syscall142(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in unsafe { select(nfds, readfds_ptr, writefds_ptr, 0 as _, 0 as _) } } +/// fdatasync +pub fn ___syscall148(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall148 (fdatasync) {}", _which); + + let fd: i32 = varargs.get(ctx); + + unsafe { fdatasync(fd) } +} + // setpgid pub fn ___syscall57(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall57 (setpgid) {}", _which); @@ -554,3 +767,48 @@ pub fn ___syscall122(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as *mut utsname; unsafe { uname(buf_addr) } } + +/// lstat64 +pub fn ___syscall196(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 { + debug!("emscripten::___syscall196 (lstat64) {}", _which); + let path_ptr: c_int = varargs.get(ctx); + let buf_ptr: u32 = varargs.get(ctx); + let path = emscripten_memory_pointer!(ctx.memory(0), path_ptr) as *const i8; + unsafe { + let mut stat: stat = std::mem::zeroed(); + + #[cfg(target_os = "macos")] + let stat_ptr = &mut stat as *mut stat as *mut c_void; + #[cfg(not(target_os = "macos"))] + let stat_ptr = &mut stat as *mut stat; + + #[cfg(target_os = "macos")] + let ret = lstat64(path, stat_ptr); + #[cfg(not(target_os = "macos"))] + let ret = lstat(path, stat_ptr); + + debug!("ret: {}", ret); + if ret != 0 { + return ret; + } + utils::copy_stat_into_wasm(ctx, buf_ptr, &stat); + } + 0 +} + +/// fallocate +pub fn ___syscall324(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall324 (fallocate) {}", _which); + let _fd: c_int = varargs.get(ctx); + let _mode: c_int = varargs.get(ctx); + let _offset: off_t = varargs.get(ctx); + let _len: off_t = varargs.get(ctx); + #[cfg(not(target_os = "macos"))] + unsafe { + fallocate(_fd, _mode, _offset, _len) + } + #[cfg(target_os = "macos")] + { + unimplemented!() + } +} diff --git a/lib/emscripten/src/syscalls/windows.rs b/lib/emscripten/src/syscalls/windows.rs index bd32d3b53..a7ac8c11c 100644 --- a/lib/emscripten/src/syscalls/windows.rs +++ b/lib/emscripten/src/syscalls/windows.rs @@ -4,18 +4,20 @@ 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; +#[allow(non_camel_case_types)] 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); + #[cfg(not(feature = "debug"))] + let _ = which; let pathname: u32 = varargs.get(ctx); let flags: i32 = varargs.get(ctx); let mode: u32 = varargs.get(ctx); @@ -33,15 +35,15 @@ pub fn ___syscall5(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { 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); + let _ = urandom_file.write_all(&random_bytes).unwrap(); // 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 + "=> pathname: {}, flags: {}, mode: {} = fd: {}", + pathname, flags, mode, fd ); fd } @@ -56,21 +58,78 @@ pub fn ___syscall5(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { } } +/// link +pub fn ___syscall9(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall9 (link) {}", _which); + unimplemented!() +} + +/// ftruncate64 +pub fn ___syscall194(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { + debug!("emscripten::___syscall194 - stub"); + unimplemented!() +} + // chown -pub fn ___syscall212(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { +pub fn ___syscall212(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_int { debug!("emscripten::___syscall212 (chown) {}", which); + #[cfg(not(feature = "debug"))] + let _ = which; -1 } +/// access +pub fn ___syscall33(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall33 (access) {}", _which); + unimplemented!() +} + +/// nice +pub fn ___syscall34(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall34 (nice) {}", _which); + unimplemented!() +} + // mkdir pub fn ___syscall39(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall39 (mkdir) {}", which); + #[cfg(not(feature = "debug"))] + let _ = 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; unsafe { mkdir(pathname_addr) } } +/// dup +pub fn ___syscall41(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall41 (dup) {}", _which); + unimplemented!() +} + +/// getrusage +pub fn ___syscall77(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall77 (getrusage) {}", _which); + unimplemented!() +} + +/// symlink +pub fn ___syscall83(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall83 (symlink) {}", _which); + unimplemented!() +} + +/// lchown +pub fn ___syscall198(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall198 (lchown) {}", _which); + unimplemented!() +} + +/// getgid +pub fn ___syscall200(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { + debug!("emscripten::___syscall200 (getgid)"); + unimplemented!() +} + // getgid pub fn ___syscall201(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall201 (getgid)"); @@ -84,60 +143,122 @@ pub fn ___syscall202(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { -1 } +/// getgroups +pub fn ___syscall205(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall205 (getgroups) {}", _which); + unimplemented!() +} + +/// madvise +pub fn ___syscall219(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall212 (chown) {}", _which); + unimplemented!() +} + /// dup3 -pub fn ___syscall330(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> 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(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { +pub fn ___syscall54(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_int { debug!("emscripten::___syscall54 (ioctl) {}", which); + #[cfg(not(feature = "debug"))] + let _ = which; -1 } +/// fchmod +pub fn ___syscall94(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall118 (fchmod) {}", _which); + unimplemented!() +} + // socketcall #[allow(clippy::cast_ptr_alignment)] -pub fn ___syscall102(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { +pub fn ___syscall102(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_int { debug!("emscripten::___syscall102 (socketcall) {}", which); + #[cfg(not(feature = "debug"))] + let _ = which; -1 } +/// fsync +pub fn ___syscall118(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall118 (fsync) {}", _which); + unimplemented!() +} + // pread -pub fn ___syscall180(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { +pub fn ___syscall180(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_int { debug!("emscripten::___syscall180 (pread) {}", which); + #[cfg(not(feature = "debug"))] + let _ = which; -1 } // pwrite -pub fn ___syscall181(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { +pub fn ___syscall181(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_int { debug!("emscripten::___syscall181 (pwrite) {}", which); + #[cfg(not(feature = "debug"))] + let _ = which; -1 } /// wait4 #[allow(clippy::cast_ptr_alignment)] -pub fn ___syscall114(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> 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(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { +pub fn ___syscall142(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_int { debug!("emscripten::___syscall142 (newselect) {}", which); + #[cfg(not(feature = "debug"))] + let _ = which; -1 } +/// fdatasync +pub fn ___syscall148(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall148 (fdatasync) {}", _which); + unimplemented!(); +} + // setpgid -pub fn ___syscall57(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { +pub fn ___syscall57(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_int { debug!("emscripten::___syscall57 (setpgid) {}", which); + #[cfg(not(feature = "debug"))] + let _ = which; -1 } /// uname // NOTE: Wondering if we should return custom utsname, like Emscripten. -pub fn ___syscall122(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { +pub fn ___syscall122(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_int { debug!("emscripten::___syscall122 (uname) {}", which); + #[cfg(not(feature = "debug"))] + let _ = which; -1 } + +/// lstat64 +pub fn ___syscall196(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { + debug!("emscripten::___syscall196 (lstat64) - stub"); + -1 +} + +/// fchown +pub fn ___syscall207(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall207 (fchown) {}", _which); + unimplemented!() +} + +/// fallocate +pub fn ___syscall324(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall324 (fallocate) {}", _which); + unimplemented!() +} diff --git a/lib/emscripten/src/time.rs b/lib/emscripten/src/time.rs index 459124f10..32e7a61c6 100644 --- a/lib/emscripten/src/time.rs +++ b/lib/emscripten/src/time.rs @@ -10,6 +10,7 @@ use libc::{clockid_t, time as libc_time}; use libc::time_t; #[cfg(target_os = "windows")] +#[allow(non_camel_case_types)] type clockid_t = c_int; #[cfg(target_os = "windows")] @@ -27,7 +28,9 @@ use wasmer_runtime_core::vm::Ctx; use libc::{CLOCK_MONOTONIC, CLOCK_MONOTONIC_COARSE, CLOCK_REALTIME}; #[cfg(target_os = "macos")] -use libc::{CLOCK_MONOTONIC, CLOCK_REALTIME}; +use libc::CLOCK_REALTIME; +#[cfg(target_os = "macos")] +const CLOCK_MONOTONIC: clockid_t = 1; #[cfg(target_os = "macos")] const CLOCK_MONOTONIC_COARSE: clockid_t = 6; @@ -297,15 +300,77 @@ pub fn _time(ctx: &mut Ctx, time_p: u32) -> i32 { /// emscripten: _strftime pub fn _strftime( - _ctx: &mut Ctx, - _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 + + #[allow(clippy::cast_ptr_alignment)] + let s = emscripten_memory_pointer!(ctx.memory(0), s_ptr) as *mut c_char; + #[allow(clippy::cast_ptr_alignment)] + let format = emscripten_memory_pointer!(ctx.memory(0), format_ptr) as *const c_char; + #[allow(clippy::cast_ptr_alignment)] + let tm = emscripten_memory_pointer!(ctx.memory(0), tm_ptr) as *const guest_tm; + + let format_string = unsafe { std::ffi::CStr::from_ptr(format).to_str().unwrap() }; + + debug!("=> format_string: {:?}", format_string); + + let tm = unsafe { &*tm }; + + let rust_tm = ::time::Tm { + tm_sec: tm.tm_sec, + tm_min: tm.tm_min, + tm_hour: tm.tm_hour, + tm_mday: tm.tm_mday, + tm_mon: tm.tm_mon, + tm_year: tm.tm_year, + tm_wday: tm.tm_wday, + tm_yday: tm.tm_yday, + tm_isdst: tm.tm_isdst, + tm_utcoff: tm.tm_gmtoff, + tm_nsec: 0, + }; + + let result_str = match ::time::strftime(format_string, &rust_tm) { + Ok(res_string) => res_string, + // TODO: maybe match on e in Err(e) and return different values if required + _ => return 0, + }; + + // pad for null? + let bytes = result_str.chars().count(); + if bytes as u32 > maxsize { + 0 + } else { + // write output string + for (i, c) in result_str.chars().enumerate() { + unsafe { *s.add(i) = c as c_char }; + } + // null terminate? + bytes as i32 + } +} + +/// emscripten: _strftime_l +pub fn _strftime_l( + ctx: &mut Ctx, + s_ptr: c_int, + maxsize: u32, + format_ptr: c_int, + tm_ptr: c_int, + _last: c_int, +) -> i32 { + debug!( + "emscripten::_strftime_l {} {} {} {}", + s_ptr, maxsize, format_ptr, tm_ptr + ); + + _strftime(ctx, s_ptr, maxsize, format_ptr, tm_ptr) } diff --git a/lib/emscripten/src/utils.rs b/lib/emscripten/src/utils.rs index 79936f2f4..2ad5b9a40 100644 --- a/lib/emscripten/src/utils.rs +++ b/lib/emscripten/src/utils.rs @@ -1,5 +1,6 @@ use super::env; use super::env::get_emscripten_data; +use crate::storage::align_memory; use libc::stat; use std::ffi::CStr; use std::mem::size_of; @@ -39,6 +40,43 @@ pub fn get_emscripten_memory_size(module: &Module) -> (Pages, Option) { (memory.minimum, memory.maximum) } +/// Reads values written by `-s EMIT_EMSCRIPTEN_METADATA=1` +/// Assumes values start from the end in this order: +/// Last export: Dynamic Base +/// Second-to-Last export: Dynamic top pointer +pub fn get_emscripten_metadata(module: &Module) -> Option<(u32, u32)> { + let max_idx = &module.info().globals.iter().map(|(k, _)| k).max()?; + let snd_max_idx = &module + .info() + .globals + .iter() + .map(|(k, _)| k) + .filter(|k| k != max_idx) + .max()?; + + use wasmer_runtime_core::types::{GlobalInit, Initializer::Const, Value::I32}; + if let ( + GlobalInit { + init: Const(I32(dynamic_base)), + .. + }, + GlobalInit { + init: Const(I32(dynamictop_ptr)), + .. + }, + ) = ( + &module.info().globals[*max_idx], + &module.info().globals[*snd_max_idx], + ) { + Some(( + align_memory(*dynamic_base as u32 - 32), + align_memory(*dynamictop_ptr as u32 - 32), + )) + } else { + None + } +} + 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; @@ -90,6 +128,7 @@ pub unsafe fn allocate_cstr_on_stack<'a>(ctx: &'a mut Ctx, s: &str) -> (u32, &'a (offset, slice) } +#[cfg(not(target_os = "windows"))] pub unsafe fn copy_terminated_array_of_cstrs(_ctx: &mut Ctx, cstrs: *mut *mut c_char) -> u32 { let _total_num = { let mut ptr = cstrs; @@ -124,7 +163,7 @@ pub struct GuestStat { st_atime: u64, st_mtime: u64, st_ctime: u64, - st_ino: u64, + st_ino: u32, } #[allow(clippy::cast_ptr_alignment)] @@ -185,7 +224,13 @@ mod tests { LLVMCompiler::new() } - #[cfg(not(any(feature = "llvm", feature = "clif")))] + #[cfg(feature = "singlepass")] + fn get_compiler() -> impl Compiler { + use wasmer_singlepass_backend::SinglePassCompiler; + SinglePassCompiler::new() + } + + #[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))] fn get_compiler() -> impl Compiler { panic!("compiler not specified, activate a compiler via features"); use wasmer_clif_backend::CraneliftCompiler; diff --git a/lib/emscripten/src/varargs.rs b/lib/emscripten/src/varargs.rs index cd9073cb9..a6a1fe46b 100644 --- a/lib/emscripten/src/varargs.rs +++ b/lib/emscripten/src/varargs.rs @@ -1,8 +1,5 @@ use std::mem; -use wasmer_runtime_core::{ - types::{Type, WasmExternType}, - vm::Ctx, -}; +use wasmer_runtime_core::{types::WasmExternType, vm::Ctx}; #[repr(transparent)] #[derive(Copy, Clone)] @@ -19,5 +16,12 @@ impl VarArgs { } unsafe impl WasmExternType for VarArgs { - const TYPE: Type = Type::I32; + type Native = i32; + + fn to_native(self) -> Self::Native { + self.pointer as _ + } + fn from_native(n: Self::Native) -> Self { + Self { pointer: n as u32 } + } } diff --git a/lib/emscripten/tests/emtests/_common.rs b/lib/emscripten/tests/emtests/_common.rs index e7a42b356..c2e304e32 100644 --- a/lib/emscripten/tests/emtests/_common.rs +++ b/lib/emscripten/tests/emtests/_common.rs @@ -20,7 +20,13 @@ macro_rules! assert_emscripten_output { LLVMCompiler::new() } - #[cfg(not(any(feature = "llvm", feature = "clif")))] + #[cfg(feature = "singlepass")] + fn get_compiler() -> impl Compiler { + use wasmer_singlepass_backend::SinglePassCompiler; + SinglePassCompiler::new() + } + + #[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))] fn get_compiler() -> impl Compiler { panic!("compiler not specified, activate a compiler via features"); use wasmer_clif_backend::CraneliftCompiler; @@ -47,6 +53,7 @@ macro_rules! assert_emscripten_output { &mut instance, $name, $args, + None, ).expect("run_emscripten_instance finishes"); let output = capturer.end().unwrap().0; @@ -61,36 +68,36 @@ 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}; +// 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 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 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(); +// let capturer = StdioCapturer::new(); - wasmer_emscripten::run_emscripten_instance(&module, &mut instance, "test", vec![]) - .expect("run_emscripten_instance finishes"); +// wasmer_emscripten::run_emscripten_instance(&module, &mut instance, "test", vec![]) +// .expect("run_emscripten_instance finishes"); - let raw_output_string = capturer.end().unwrap().0; +// 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(); +// // 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); +// let contains_output = output.contains(expected_output); - assert!( - contains_output, - "Output: `{}` does not contain expected output: `{}`", - output, 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 89a66bf2b..645351227 100644 --- a/lib/emscripten/tests/emtests/mod.rs +++ b/lib/emscripten/tests/emtests/mod.rs @@ -176,6 +176,7 @@ mod test_unary_literal; mod test_utf; mod test_varargs; mod test_varargs_multi; +mod test_vfs; mod test_vprintf; mod test_vsnprintf; mod test_wprintf; diff --git a/lib/emscripten/tests/emtests/test_execvp.rs b/lib/emscripten/tests/emtests/test_execvp.rs index c9b4b5113..085e41d9a 100644 --- a/lib/emscripten/tests/emtests/test_execvp.rs +++ b/lib/emscripten/tests/emtests/test_execvp.rs @@ -1,17 +1,10 @@ #[test] -fn test_execvp() { - #[cfg(not(target_os = "windows"))] +#[ignore] +fn test_test_execvp() { 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 index ec770b505..98723df87 100644 --- a/lib/emscripten/tests/emtests/test_getcwd.rs +++ b/lib/emscripten/tests/emtests/test_getcwd.rs @@ -1,8 +1,8 @@ #[test] -fn test_getcwd() { +fn test_test_getcwd() { assert_emscripten_output!( "../../emtests/test_getcwd.wasm", - "getcwd", + "test_getcwd", vec![], "../../emtests/test_getcwd.out" ); diff --git a/lib/emscripten/tests/emtests/test_longjmp.rs b/lib/emscripten/tests/emtests/test_longjmp.rs index c125f5d21..90aaba8ff 100644 --- a/lib/emscripten/tests/emtests/test_longjmp.rs +++ b/lib/emscripten/tests/emtests/test_longjmp.rs @@ -1,5 +1,4 @@ #[test] -#[ignore] fn test_test_longjmp() { assert_emscripten_output!( "../../emtests/test_longjmp.wasm", diff --git a/lib/emscripten/tests/emtests/test_longjmp2.rs b/lib/emscripten/tests/emtests/test_longjmp2.rs index 44989e08f..3ad2c66c6 100644 --- a/lib/emscripten/tests/emtests/test_longjmp2.rs +++ b/lib/emscripten/tests/emtests/test_longjmp2.rs @@ -1,5 +1,4 @@ #[test] -#[ignore] fn test_test_longjmp2() { assert_emscripten_output!( "../../emtests/test_longjmp2.wasm", diff --git a/lib/emscripten/tests/emtests/test_longjmp3.rs b/lib/emscripten/tests/emtests/test_longjmp3.rs index 4ce0e2e6f..65923c8ed 100644 --- a/lib/emscripten/tests/emtests/test_longjmp3.rs +++ b/lib/emscripten/tests/emtests/test_longjmp3.rs @@ -1,5 +1,4 @@ #[test] -#[ignore] fn test_test_longjmp3() { assert_emscripten_output!( "../../emtests/test_longjmp3.wasm", diff --git a/lib/emscripten/tests/emtests/test_longjmp4.rs b/lib/emscripten/tests/emtests/test_longjmp4.rs index f3c53ea56..3eabe4099 100644 --- a/lib/emscripten/tests/emtests/test_longjmp4.rs +++ b/lib/emscripten/tests/emtests/test_longjmp4.rs @@ -1,5 +1,4 @@ #[test] -#[ignore] fn test_test_longjmp4() { assert_emscripten_output!( "../../emtests/test_longjmp4.wasm", diff --git a/lib/emscripten/tests/emtests/test_longjmp_funcptr.rs b/lib/emscripten/tests/emtests/test_longjmp_funcptr.rs index a40316558..85a69d8ed 100644 --- a/lib/emscripten/tests/emtests/test_longjmp_funcptr.rs +++ b/lib/emscripten/tests/emtests/test_longjmp_funcptr.rs @@ -1,5 +1,4 @@ #[test] -#[ignore] fn test_test_longjmp_funcptr() { assert_emscripten_output!( "../../emtests/test_longjmp_funcptr.wasm", diff --git a/lib/emscripten/tests/emtests/test_longjmp_repeat.rs b/lib/emscripten/tests/emtests/test_longjmp_repeat.rs index 6fbc65527..5d2357db7 100644 --- a/lib/emscripten/tests/emtests/test_longjmp_repeat.rs +++ b/lib/emscripten/tests/emtests/test_longjmp_repeat.rs @@ -1,5 +1,4 @@ #[test] -#[ignore] fn test_test_longjmp_repeat() { assert_emscripten_output!( "../../emtests/test_longjmp_repeat.wasm", diff --git a/lib/emscripten/tests/emtests/test_longjmp_stacked.rs b/lib/emscripten/tests/emtests/test_longjmp_stacked.rs index b145e6da4..e5a69fb17 100644 --- a/lib/emscripten/tests/emtests/test_longjmp_stacked.rs +++ b/lib/emscripten/tests/emtests/test_longjmp_stacked.rs @@ -1,5 +1,4 @@ #[test] -#[ignore] fn test_test_longjmp_stacked() { assert_emscripten_output!( "../../emtests/test_longjmp_stacked.wasm", diff --git a/lib/emscripten/tests/emtests/test_longjmp_throw.rs b/lib/emscripten/tests/emtests/test_longjmp_throw.rs index 5f97462ad..877c620ef 100644 --- a/lib/emscripten/tests/emtests/test_longjmp_throw.rs +++ b/lib/emscripten/tests/emtests/test_longjmp_throw.rs @@ -1,5 +1,4 @@ #[test] -#[ignore] fn test_test_longjmp_throw() { assert_emscripten_output!( "../../emtests/test_longjmp_throw.wasm", diff --git a/lib/emscripten/tests/emtests/test_longjmp_unwind.rs b/lib/emscripten/tests/emtests/test_longjmp_unwind.rs index eec134135..f6e921396 100644 --- a/lib/emscripten/tests/emtests/test_longjmp_unwind.rs +++ b/lib/emscripten/tests/emtests/test_longjmp_unwind.rs @@ -1,5 +1,4 @@ #[test] -#[ignore] fn test_test_longjmp_unwind() { assert_emscripten_output!( "../../emtests/test_longjmp_unwind.wasm", diff --git a/lib/emscripten/tests/emtests/test_nl_types.rs b/lib/emscripten/tests/emtests/test_nl_types.rs index 101ee4894..68b81c562 100644 --- a/lib/emscripten/tests/emtests/test_nl_types.rs +++ b/lib/emscripten/tests/emtests/test_nl_types.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_nl_types() { assert_emscripten_output!( "../../emtests/test_nl_types.wasm", diff --git a/lib/emscripten/tests/emtests/test_phiundef.rs b/lib/emscripten/tests/emtests/test_phiundef.rs index fbe4203f7..e3cba2c0f 100644 --- a/lib/emscripten/tests/emtests/test_phiundef.rs +++ b/lib/emscripten/tests/emtests/test_phiundef.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_phiundef() { assert_emscripten_output!( "../../emtests/test_phiundef.wasm", diff --git a/lib/emscripten/tests/emtests/test_pipe.rs b/lib/emscripten/tests/emtests/test_pipe.rs index 9ffe0c828..e7caee28b 100644 --- a/lib/emscripten/tests/emtests/test_pipe.rs +++ b/lib/emscripten/tests/emtests/test_pipe.rs @@ -1,8 +1,10 @@ -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); +#[ignore] +fn test_test_pipe() { + assert_emscripten_output!( + "../../emtests/test_pipe.wasm", + "test_pipe", + vec![], + "../../emtests/test_pipe.out" + ); } diff --git a/lib/emscripten/tests/emtests/test_printf_2.rs b/lib/emscripten/tests/emtests/test_printf_2.rs index 62151b8a4..ac15139bf 100644 --- a/lib/emscripten/tests/emtests/test_printf_2.rs +++ b/lib/emscripten/tests/emtests/test_printf_2.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_printf_2() { assert_emscripten_output!( "../../emtests/test_printf_2.wasm", diff --git a/lib/emscripten/tests/emtests/test_printf_more.rs b/lib/emscripten/tests/emtests/test_printf_more.rs index 6dc34233f..b1e4fc00f 100644 --- a/lib/emscripten/tests/emtests/test_printf_more.rs +++ b/lib/emscripten/tests/emtests/test_printf_more.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_printf_more() { assert_emscripten_output!( "../../emtests/test_printf_more.wasm", diff --git a/lib/emscripten/tests/emtests/test_regex.rs b/lib/emscripten/tests/emtests/test_regex.rs index 03ab62295..ae65d8ef9 100644 --- a/lib/emscripten/tests/emtests/test_regex.rs +++ b/lib/emscripten/tests/emtests/test_regex.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_regex() { assert_emscripten_output!( "../../emtests/test_regex.wasm", diff --git a/lib/emscripten/tests/emtests/test_relocatable_void_function.rs b/lib/emscripten/tests/emtests/test_relocatable_void_function.rs index 7cdd382b8..77dda77ba 100644 --- a/lib/emscripten/tests/emtests/test_relocatable_void_function.rs +++ b/lib/emscripten/tests/emtests/test_relocatable_void_function.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_relocatable_void_function() { assert_emscripten_output!( "../../emtests/test_relocatable_void_function.wasm", diff --git a/lib/emscripten/tests/emtests/test_rounding.rs b/lib/emscripten/tests/emtests/test_rounding.rs index defd8df6f..cbe0a8ff5 100644 --- a/lib/emscripten/tests/emtests/test_rounding.rs +++ b/lib/emscripten/tests/emtests/test_rounding.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_rounding() { assert_emscripten_output!( "../../emtests/test_rounding.wasm", diff --git a/lib/emscripten/tests/emtests/test_set_align.rs b/lib/emscripten/tests/emtests/test_set_align.rs index 039d7f0c8..47aa7c3dd 100644 --- a/lib/emscripten/tests/emtests/test_set_align.rs +++ b/lib/emscripten/tests/emtests/test_set_align.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_set_align() { assert_emscripten_output!( "../../emtests/test_set_align.wasm", diff --git a/lib/emscripten/tests/emtests/test_sintvars.rs b/lib/emscripten/tests/emtests/test_sintvars.rs index cf506e10c..f74daa8b1 100644 --- a/lib/emscripten/tests/emtests/test_sintvars.rs +++ b/lib/emscripten/tests/emtests/test_sintvars.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_sintvars() { assert_emscripten_output!( "../../emtests/test_sintvars.wasm", diff --git a/lib/emscripten/tests/emtests/test_sizeof.rs b/lib/emscripten/tests/emtests/test_sizeof.rs index 516f0c58f..53e8cf19f 100644 --- a/lib/emscripten/tests/emtests/test_sizeof.rs +++ b/lib/emscripten/tests/emtests/test_sizeof.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_sizeof() { assert_emscripten_output!( "../../emtests/test_sizeof.wasm", diff --git a/lib/emscripten/tests/emtests/test_sscanf.rs b/lib/emscripten/tests/emtests/test_sscanf.rs index 72353b9db..e3072841f 100644 --- a/lib/emscripten/tests/emtests/test_sscanf.rs +++ b/lib/emscripten/tests/emtests/test_sscanf.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_sscanf() { assert_emscripten_output!( "../../emtests/test_sscanf.wasm", diff --git a/lib/emscripten/tests/emtests/test_sscanf_3.rs b/lib/emscripten/tests/emtests/test_sscanf_3.rs index 816045fd5..c331db881 100644 --- a/lib/emscripten/tests/emtests/test_sscanf_3.rs +++ b/lib/emscripten/tests/emtests/test_sscanf_3.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_sscanf_3() { assert_emscripten_output!( "../../emtests/test_sscanf_3.wasm", diff --git a/lib/emscripten/tests/emtests/test_sscanf_4.rs b/lib/emscripten/tests/emtests/test_sscanf_4.rs index d859d1767..a9a959f38 100644 --- a/lib/emscripten/tests/emtests/test_sscanf_4.rs +++ b/lib/emscripten/tests/emtests/test_sscanf_4.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_sscanf_4() { assert_emscripten_output!( "../../emtests/test_sscanf_4.wasm", diff --git a/lib/emscripten/tests/emtests/test_sscanf_5.rs b/lib/emscripten/tests/emtests/test_sscanf_5.rs index 22d4d5315..05801407f 100644 --- a/lib/emscripten/tests/emtests/test_sscanf_5.rs +++ b/lib/emscripten/tests/emtests/test_sscanf_5.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_sscanf_5() { assert_emscripten_output!( "../../emtests/test_sscanf_5.wasm", diff --git a/lib/emscripten/tests/emtests/test_sscanf_6.rs b/lib/emscripten/tests/emtests/test_sscanf_6.rs index 52be26949..d9771ca13 100644 --- a/lib/emscripten/tests/emtests/test_sscanf_6.rs +++ b/lib/emscripten/tests/emtests/test_sscanf_6.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_sscanf_6() { assert_emscripten_output!( "../../emtests/test_sscanf_6.wasm", diff --git a/lib/emscripten/tests/emtests/test_sscanf_caps.rs b/lib/emscripten/tests/emtests/test_sscanf_caps.rs index 5f2d64b90..f88ec7e76 100644 --- a/lib/emscripten/tests/emtests/test_sscanf_caps.rs +++ b/lib/emscripten/tests/emtests/test_sscanf_caps.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_sscanf_caps() { assert_emscripten_output!( "../../emtests/test_sscanf_caps.wasm", diff --git a/lib/emscripten/tests/emtests/test_sscanf_float.rs b/lib/emscripten/tests/emtests/test_sscanf_float.rs index 39d3b5cba..f98c0b2d0 100644 --- a/lib/emscripten/tests/emtests/test_sscanf_float.rs +++ b/lib/emscripten/tests/emtests/test_sscanf_float.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_sscanf_float() { assert_emscripten_output!( "../../emtests/test_sscanf_float.wasm", diff --git a/lib/emscripten/tests/emtests/test_sscanf_n.rs b/lib/emscripten/tests/emtests/test_sscanf_n.rs index 57f90b142..596c70307 100644 --- a/lib/emscripten/tests/emtests/test_sscanf_n.rs +++ b/lib/emscripten/tests/emtests/test_sscanf_n.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_sscanf_n() { assert_emscripten_output!( "../../emtests/test_sscanf_n.wasm", diff --git a/lib/emscripten/tests/emtests/test_strcasecmp.rs b/lib/emscripten/tests/emtests/test_strcasecmp.rs index 294017872..4c0a9ed9e 100644 --- a/lib/emscripten/tests/emtests/test_strcasecmp.rs +++ b/lib/emscripten/tests/emtests/test_strcasecmp.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strcasecmp() { assert_emscripten_output!( "../../emtests/test_strcasecmp.wasm", diff --git a/lib/emscripten/tests/emtests/test_strcmp_uni.rs b/lib/emscripten/tests/emtests/test_strcmp_uni.rs index e729c00ae..5691e4f57 100644 --- a/lib/emscripten/tests/emtests/test_strcmp_uni.rs +++ b/lib/emscripten/tests/emtests/test_strcmp_uni.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strcmp_uni() { assert_emscripten_output!( "../../emtests/test_strcmp_uni.wasm", diff --git a/lib/emscripten/tests/emtests/test_strndup.rs b/lib/emscripten/tests/emtests/test_strndup.rs index 62ac406e9..7fdb25bbe 100644 --- a/lib/emscripten/tests/emtests/test_strndup.rs +++ b/lib/emscripten/tests/emtests/test_strndup.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strndup() { assert_emscripten_output!( "../../emtests/test_strndup.wasm", diff --git a/lib/emscripten/tests/emtests/test_strstr.rs b/lib/emscripten/tests/emtests/test_strstr.rs index 080d3bbda..86af149e8 100644 --- a/lib/emscripten/tests/emtests/test_strstr.rs +++ b/lib/emscripten/tests/emtests/test_strstr.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strstr() { assert_emscripten_output!( "../../emtests/test_strstr.wasm", diff --git a/lib/emscripten/tests/emtests/test_strtod.rs b/lib/emscripten/tests/emtests/test_strtod.rs index cab755982..52bb1987e 100644 --- a/lib/emscripten/tests/emtests/test_strtod.rs +++ b/lib/emscripten/tests/emtests/test_strtod.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strtod() { assert_emscripten_output!( "../../emtests/test_strtod.wasm", diff --git a/lib/emscripten/tests/emtests/test_strtok.rs b/lib/emscripten/tests/emtests/test_strtok.rs index 1399ef683..cef228e85 100644 --- a/lib/emscripten/tests/emtests/test_strtok.rs +++ b/lib/emscripten/tests/emtests/test_strtok.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strtok() { assert_emscripten_output!( "../../emtests/test_strtok.wasm", diff --git a/lib/emscripten/tests/emtests/test_strtol_bin.rs b/lib/emscripten/tests/emtests/test_strtol_bin.rs index fd847c186..48f459556 100644 --- a/lib/emscripten/tests/emtests/test_strtol_bin.rs +++ b/lib/emscripten/tests/emtests/test_strtol_bin.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strtol_bin() { assert_emscripten_output!( "../../emtests/test_strtol_bin.wasm", diff --git a/lib/emscripten/tests/emtests/test_strtol_dec.rs b/lib/emscripten/tests/emtests/test_strtol_dec.rs index a485bfa97..7a3a6e89a 100644 --- a/lib/emscripten/tests/emtests/test_strtol_dec.rs +++ b/lib/emscripten/tests/emtests/test_strtol_dec.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strtol_dec() { assert_emscripten_output!( "../../emtests/test_strtol_dec.wasm", diff --git a/lib/emscripten/tests/emtests/test_strtol_hex.rs b/lib/emscripten/tests/emtests/test_strtol_hex.rs index 57f108259..257e14b35 100644 --- a/lib/emscripten/tests/emtests/test_strtol_hex.rs +++ b/lib/emscripten/tests/emtests/test_strtol_hex.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strtol_hex() { assert_emscripten_output!( "../../emtests/test_strtol_hex.wasm", diff --git a/lib/emscripten/tests/emtests/test_strtol_oct.rs b/lib/emscripten/tests/emtests/test_strtol_oct.rs index 9e4340f04..ba9fa1a78 100644 --- a/lib/emscripten/tests/emtests/test_strtol_oct.rs +++ b/lib/emscripten/tests/emtests/test_strtol_oct.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strtol_oct() { assert_emscripten_output!( "../../emtests/test_strtol_oct.wasm", diff --git a/lib/emscripten/tests/emtests/test_strtoll_bin.rs b/lib/emscripten/tests/emtests/test_strtoll_bin.rs index 1276f0c85..0e067ef72 100644 --- a/lib/emscripten/tests/emtests/test_strtoll_bin.rs +++ b/lib/emscripten/tests/emtests/test_strtoll_bin.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strtoll_bin() { assert_emscripten_output!( "../../emtests/test_strtoll_bin.wasm", diff --git a/lib/emscripten/tests/emtests/test_strtoll_dec.rs b/lib/emscripten/tests/emtests/test_strtoll_dec.rs index 6e902d0ed..a073eb05d 100644 --- a/lib/emscripten/tests/emtests/test_strtoll_dec.rs +++ b/lib/emscripten/tests/emtests/test_strtoll_dec.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strtoll_dec() { assert_emscripten_output!( "../../emtests/test_strtoll_dec.wasm", diff --git a/lib/emscripten/tests/emtests/test_strtoll_hex.rs b/lib/emscripten/tests/emtests/test_strtoll_hex.rs index cc98e0fd0..8bb61fa1b 100644 --- a/lib/emscripten/tests/emtests/test_strtoll_hex.rs +++ b/lib/emscripten/tests/emtests/test_strtoll_hex.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strtoll_hex() { assert_emscripten_output!( "../../emtests/test_strtoll_hex.wasm", diff --git a/lib/emscripten/tests/emtests/test_strtoll_oct.rs b/lib/emscripten/tests/emtests/test_strtoll_oct.rs index 13ed7f5b2..8a96615d6 100644 --- a/lib/emscripten/tests/emtests/test_strtoll_oct.rs +++ b/lib/emscripten/tests/emtests/test_strtoll_oct.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strtoll_oct() { assert_emscripten_output!( "../../emtests/test_strtoll_oct.wasm", diff --git a/lib/emscripten/tests/emtests/test_struct_varargs.rs b/lib/emscripten/tests/emtests/test_struct_varargs.rs index 71f66595b..211164a29 100644 --- a/lib/emscripten/tests/emtests/test_struct_varargs.rs +++ b/lib/emscripten/tests/emtests/test_struct_varargs.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_struct_varargs() { assert_emscripten_output!( "../../emtests/test_struct_varargs.wasm", diff --git a/lib/emscripten/tests/emtests/test_transtrcase.rs b/lib/emscripten/tests/emtests/test_transtrcase.rs index f9d169287..d0c7079c9 100644 --- a/lib/emscripten/tests/emtests/test_transtrcase.rs +++ b/lib/emscripten/tests/emtests/test_transtrcase.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_transtrcase() { assert_emscripten_output!( "../../emtests/test_transtrcase.wasm", diff --git a/lib/emscripten/tests/emtests/test_trickystring.rs b/lib/emscripten/tests/emtests/test_trickystring.rs index cf5698528..f04295c92 100644 --- a/lib/emscripten/tests/emtests/test_trickystring.rs +++ b/lib/emscripten/tests/emtests/test_trickystring.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_trickystring() { assert_emscripten_output!( "../../emtests/test_trickystring.wasm", diff --git a/lib/emscripten/tests/emtests/test_unary_literal.rs b/lib/emscripten/tests/emtests/test_unary_literal.rs index 06f61b92c..fc58fa288 100644 --- a/lib/emscripten/tests/emtests/test_unary_literal.rs +++ b/lib/emscripten/tests/emtests/test_unary_literal.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_unary_literal() { assert_emscripten_output!( "../../emtests/test_unary_literal.wasm", diff --git a/lib/emscripten/tests/emtests/test_vfs.rs b/lib/emscripten/tests/emtests/test_vfs.rs new file mode 100644 index 000000000..afd339042 --- /dev/null +++ b/lib/emscripten/tests/emtests/test_vfs.rs @@ -0,0 +1,10 @@ +#[test] +#[ignore] +fn test_test_vfs() { + assert_emscripten_output!( + "../../emtests/test_vfs.wasm", + "test_vfs", + vec![], + "../../emtests/test_vfs.out" + ); +} diff --git a/lib/emscripten/tests/emtests/test_vprintf.rs b/lib/emscripten/tests/emtests/test_vprintf.rs index 1a5cdb142..18fa63018 100644 --- a/lib/emscripten/tests/emtests/test_vprintf.rs +++ b/lib/emscripten/tests/emtests/test_vprintf.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_vprintf() { assert_emscripten_output!( "../../emtests/test_vprintf.wasm", diff --git a/lib/emscripten/tests/emtests/test_vsnprintf.rs b/lib/emscripten/tests/emtests/test_vsnprintf.rs index c5cbbf091..f11254464 100644 --- a/lib/emscripten/tests/emtests/test_vsnprintf.rs +++ b/lib/emscripten/tests/emtests/test_vsnprintf.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_vsnprintf() { assert_emscripten_output!( "../../emtests/test_vsnprintf.wasm", diff --git a/lib/emscripten/tests/emtests/test_write_stdout_fileno.rs b/lib/emscripten/tests/emtests/test_write_stdout_fileno.rs index df3da00a5..debdc6530 100644 --- a/lib/emscripten/tests/emtests/test_write_stdout_fileno.rs +++ b/lib/emscripten/tests/emtests/test_write_stdout_fileno.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_write_stdout_fileno() { assert_emscripten_output!( "../../emtests/test_write_stdout_fileno.wasm", diff --git a/lib/emscripten/tests/emtests/test_zerodiv.rs b/lib/emscripten/tests/emtests/test_zerodiv.rs index 86712f6b5..df68359d3 100644 --- a/lib/emscripten/tests/emtests/test_zerodiv.rs +++ b/lib/emscripten/tests/emtests/test_zerodiv.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_zerodiv() { assert_emscripten_output!( "../../emtests/test_zerodiv.wasm", diff --git a/lib/llvm-backend/Cargo.toml b/lib/llvm-backend/Cargo.toml index 8be5aaa32..fabf70cef 100644 --- a/lib/llvm-backend/Cargo.toml +++ b/lib/llvm-backend/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "wasmer-llvm-backend" -version = "0.1.0" +version = "0.4.1" authors = ["Lachlan Sneff "] edition = "2018" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.2.1" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } inkwell = { git = "https://github.com/wasmerio/inkwell", branch = "llvm7-0" } -wasmparser = "0.28.0" +wasmparser = "0.29.2" hashbrown = "0.1.8" smallvec = "0.6.8" goblin = "0.0.20" @@ -27,4 +27,4 @@ wabt = "0.7.4" [features] debug = ["wasmer-runtime-core/debug"] -disasm = ["capstone"] \ No newline at end of file +disasm = ["capstone"] diff --git a/lib/llvm-backend/README.md b/lib/llvm-backend/README.md new file mode 100644 index 000000000..f53a0cb31 --- /dev/null +++ b/lib/llvm-backend/README.md @@ -0,0 +1,31 @@ +

+ + Wasmer logo + +

+ +

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

+ +# Wasmer LLVM backend + +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 represents the LLVM backend. diff --git a/lib/llvm-backend/build.rs b/lib/llvm-backend/build.rs index ed96cae9d..ab59748b1 100644 --- a/lib/llvm-backend/build.rs +++ b/lib/llvm-backend/build.rs @@ -213,6 +213,8 @@ fn main() { println!("cargo:rustc-link-lib=static=llvm-backend"); println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=cpp/object_loader.cpp"); + println!("cargo:rerun-if-changed=cpp/object_loader.hh"); // Enable "nightly" cfg if the current compiler is nightly. if rustc_version::version_meta().unwrap().channel == rustc_version::Channel::Nightly { diff --git a/lib/llvm-backend/cpp/object_loader.cpp b/lib/llvm-backend/cpp/object_loader.cpp index cef7c7214..28bea6354 100644 --- a/lib/llvm-backend/cpp/object_loader.cpp +++ b/lib/llvm-backend/cpp/object_loader.cpp @@ -175,21 +175,26 @@ WasmModule::WasmModule( callbacks_t callbacks ) : memory_manager(std::unique_ptr(new MemoryManager(callbacks))) { - object_file = llvm::cantFail(llvm::object::ObjectFile::createObjectFile(llvm::MemoryBufferRef( + + + if (auto created_object_file = llvm::object::ObjectFile::createObjectFile(llvm::MemoryBufferRef( llvm::StringRef((const char *)object_start, object_size), "object" - ))); + ))) { + object_file = cantFail(std::move(created_object_file)); + SymbolLookup symbol_resolver(callbacks); + runtime_dyld = std::unique_ptr(new llvm::RuntimeDyld(*memory_manager, symbol_resolver)); - SymbolLookup symbol_resolver(callbacks); - runtime_dyld = std::unique_ptr(new llvm::RuntimeDyld(*memory_manager, symbol_resolver)); + runtime_dyld->setProcessAllSections(true); - runtime_dyld->setProcessAllSections(true); + runtime_dyld->loadObject(*object_file); + runtime_dyld->finalizeWithMemoryManagerLocking(); - runtime_dyld->loadObject(*object_file); - runtime_dyld->finalizeWithMemoryManagerLocking(); - - if (runtime_dyld->hasError()) { - std::cout << "RuntimeDyld error: " << (std::string)runtime_dyld->getErrorString() << std::endl; - abort(); + if (runtime_dyld->hasError()) { + _init_failed = true; + return; + } + } else { + _init_failed = true; } } diff --git a/lib/llvm-backend/cpp/object_loader.hh b/lib/llvm-backend/cpp/object_loader.hh index d22acb919..63630a479 100644 --- a/lib/llvm-backend/cpp/object_loader.hh +++ b/lib/llvm-backend/cpp/object_loader.hh @@ -5,14 +5,16 @@ #include #include -typedef enum { +typedef enum +{ PROTECT_NONE, PROTECT_READ, PROTECT_READ_WRITE, PROTECT_READ_EXECUTE, } mem_protect_t; -typedef enum { +typedef enum +{ RESULT_OK, RESULT_ALLOCATE_FAILURE, RESULT_PROTECT_FAILURE, @@ -20,16 +22,17 @@ typedef enum { 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 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 void (*trampoline_t)(void *, void *, void *, void *); -typedef struct { +typedef struct +{ /* Memory management. */ alloc_memory_t alloc_memory; protect_memory_t protect_memory; @@ -40,32 +43,45 @@ typedef struct { visit_fde_t visit_fde; } callbacks_t; -struct WasmException { -public: +typedef struct +{ + size_t data, vtable; +} box_any_t; + +struct WasmException +{ + public: virtual std::string description() const noexcept = 0; }; -struct UncatchableException : WasmException { -public: - virtual std::string description() const noexcept override { +struct UncatchableException : WasmException +{ + public: + virtual std::string description() const noexcept override + { return "Uncatchable exception"; } }; -struct UserException : UncatchableException { -public: - UserException(std::string msg) : msg(msg) {} +struct UserException : UncatchableException +{ + public: + UserException(size_t data, size_t vtable) : error_data({ data, vtable }) {} - virtual std::string description() const noexcept override { - return std::string("user exception: ") + msg; + virtual std::string description() const noexcept override + { + return "user exception"; } -private: - std::string msg; + + // The parts of a `Box`. + box_any_t error_data; }; -struct WasmTrap : UncatchableException { -public: - enum Type { +struct WasmTrap : UncatchableException +{ + public: + enum Type + { Unreachable = 0, IncorrectCallIndirectSignature = 1, MemoryOutOfBounds = 2, @@ -76,49 +92,54 @@ public: WasmTrap(Type type) : type(type) {} - virtual std::string description() const noexcept override { + 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; + 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: +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 { + virtual std::string description() const noexcept override + { return "catchable exception"; } @@ -126,25 +147,33 @@ public: uint64_t values[1]; }; -struct WasmModule { -public: +struct WasmModule +{ + public: WasmModule( const uint8_t *object_start, size_t object_size, - callbacks_t callbacks - ); + callbacks_t callbacks); void *get_func(llvm::StringRef name) const; -private: + + bool _init_failed = false; + 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) { +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); + if ((*module_out)->_init_failed) { + return RESULT_OBJECT_LOAD_FAILURE; + } + return RESULT_OK; } @@ -152,34 +181,56 @@ extern "C" { throw WasmTrap(ty); } - void module_delete(WasmModule* module) { + void module_delete(WasmModule *module) + { delete module; } + // Throw a fat pointer that's assumed to be `*mut dyn Any` on the rust + // side. + [[noreturn]] void throw_any(size_t data, size_t vtable) { + throw UserException(data, vtable); + } + bool invoke_trampoline( trampoline_t trampoline, - void* ctx, - void* func, - void* params, - void* results, - WasmTrap::Type* trap_out - ) throw() { - try { + void *ctx, + void *func, + void *params, + void *results, + WasmTrap::Type *trap_out, + box_any_t *user_error, + void *invoke_env) throw() + { + try + { trampoline(ctx, func, params, results); return true; - } catch(const WasmTrap& e) { + } + catch (const WasmTrap &e) + { *trap_out = e.type; return false; - } catch(const WasmException& e) { + } + catch (const UserException &e) + { + *user_error = e.error_data; + return false; + } + catch (const WasmException &e) + { *trap_out = WasmTrap::Type::Unknown; return false; - } catch (...) { + } + catch (...) + { *trap_out = WasmTrap::Type::Unknown; return false; } } - void* get_func_symbol(WasmModule* module, const char* name) { + 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 index 742069d48..7a8c6c64a 100644 --- a/lib/llvm-backend/src/backend.rs +++ b/lib/llvm-backend/src/backend.rs @@ -11,24 +11,24 @@ use libc::{ }; use std::{ any::Any, - ffi::CString, + ffi::{c_void, CString}, mem, + ops::Deref, ptr::{self, NonNull}, slice, str, - sync::Once, + sync::{Arc, 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, + backend::{ + sys::{Memory, Protect}, + CacheGen, RunnableModule, }, - vm::{self, ImportBacking}, - vmcalls, + cache::Error as CacheError, + module::ModuleInfo, + structures::TypedIndex, + typed_func::{Wasm, WasmTrapInfo}, + types::{LocalFuncIndex, SigIndex}, + vm, vmcalls, }; #[repr(C)] @@ -57,16 +57,6 @@ enum LLVMResult { 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, @@ -89,13 +79,22 @@ extern "C" { fn throw_trap(ty: i32); + /// This should be the same as spliting up the fat pointer into two arguments, + /// but this is cleaner, I think? + #[cfg_attr(nightly, unwind(allowed))] + #[allow(improper_ctypes)] + fn throw_any(data: *mut dyn Any) -> !; + + #[allow(improper_ctypes)] fn invoke_trampoline( - trampoline: unsafe extern "C" fn(*mut vm::Ctx, *const vm::Func, *const u64, *mut u64), + trampoline: unsafe extern "C" fn(*mut vm::Ctx, NonNull, *const u64, *mut u64), vmctx_ptr: *mut vm::Ctx, - func_ptr: *const vm::Func, + func_ptr: NonNull, params: *const u64, results: *mut u64, - trap_out: *mut WasmTrapType, + trap_out: *mut WasmTrapInfo, + user_error: *mut Option>, + invoke_env: Option>, ) -> bool; } @@ -210,17 +209,32 @@ fn get_callbacks() -> Callbacks { } } +pub enum Buffer { + LlvmMemory(MemoryBuffer), + Memory(Memory), +} + +impl Deref for Buffer { + type Target = [u8]; + fn deref(&self) -> &[u8] { + match self { + Buffer::LlvmMemory(mem_buffer) => mem_buffer.as_slice(), + Buffer::Memory(memory) => unsafe { memory.as_slice() }, + } + } +} + unsafe impl Send for LLVMBackend {} unsafe impl Sync for LLVMBackend {} pub struct LLVMBackend { module: *mut LLVMModule, #[allow(dead_code)] - memory_buffer: MemoryBuffer, + buffer: Arc, } impl LLVMBackend { - pub fn new(module: Module, intrinsics: Intrinsics) -> (Self, LLVMProtectedCaller) { + pub fn new(module: Module, _intrinsics: Intrinsics) -> (Self, LLVMCache) { Target::initialize_x86(&InitializationConfig { asm_parser: true, asm_printer: true, @@ -269,16 +283,55 @@ impl LLVMBackend { panic!("failed to load object") } + let buffer = Arc::new(Buffer::LlvmMemory(memory_buffer)); + ( Self { module, - memory_buffer, + buffer: Arc::clone(&buffer), }, - LLVMProtectedCaller { module }, + LLVMCache { buffer }, ) } - pub fn get_func( + pub unsafe fn from_buffer(memory: Memory) -> Result<(Self, LLVMCache), String> { + let callbacks = get_callbacks(); + let mut module: *mut LLVMModule = ptr::null_mut(); + + let slice = memory.as_slice(); + + let res = module_load(slice.as_ptr(), slice.len(), callbacks, &mut module); + + if res != LLVMResult::OK { + return Err("failed to load object".to_string()); + } + + static SIGNAL_HANDLER_INSTALLED: Once = Once::new(); + + SIGNAL_HANDLER_INSTALLED.call_once(|| { + crate::platform::install_signal_handler(); + }); + + let buffer = Arc::new(Buffer::Memory(memory)); + + Ok(( + Self { + module, + buffer: Arc::clone(&buffer), + }, + LLVMCache { buffer }, + )) + } +} + +impl Drop for LLVMBackend { + fn drop(&mut self) { + unsafe { module_delete(self.module) } + } +} + +impl RunnableModule for LLVMBackend { + fn get_func( &self, info: &ModuleInfo, local_func_index: LocalFuncIndex, @@ -295,74 +348,14 @@ impl LLVMBackend { 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 { + fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option { + let trampoline: unsafe extern "C" fn( + *mut vm::Ctx, + NonNull, + *const u64, + *mut u64, + ) = unsafe { let name = if cfg!(target_os = "macos") { format!("_trmp{}", sig_index.index()) } else { @@ -376,99 +369,34 @@ impl ProtectedCaller for LLVMProtectedCaller { 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(), - }, - }) - } + Some(unsafe { Wasm::from_raw_parts(trampoline, invoke_trampoline, None) }) } - fn get_early_trapper(&self) -> Box { - Box::new(Placeholder) + unsafe fn do_early_trap(&self, data: Box) -> ! { + throw_any(Box::leak(data)) } } -impl UserTrapper for Placeholder { - unsafe fn do_early_trap(&self, _data: Box) -> ! { - unimplemented!("do early trap") - } +unsafe impl Send for LLVMCache {} +unsafe impl Sync for LLVMCache {} + +pub struct LLVMCache { + buffer: Arc, } -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"); +impl CacheGen for LLVMCache { + fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError> { + let mut memory = Memory::with_size_protect(self.buffer.len(), Protect::ReadWrite) + .map_err(CacheError::SerializeError)?; - 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 buffer = self.buffer.deref(); + + unsafe { + memory.as_slice_mut()[..buffer.len()].copy_from_slice(buffer); } - }; - let signature = &module.info.signatures[sig_index]; - - (func_ptr, ctx, signature, sig_index) + Ok(([].as_ref().into(), memory)) + } } #[cfg(feature = "disasm")] diff --git a/lib/llvm-backend/src/code.rs b/lib/llvm-backend/src/code.rs index 173262b67..028207a8c 100644 --- a/lib/llvm-backend/src/code.rs +++ b/lib/llvm-backend/src/code.rs @@ -3,24 +3,26 @@ use inkwell::{ context::Context, module::{Linkage, Module}, passes::PassManager, - types::{BasicType, BasicTypeEnum, FunctionType, IntType, PointerType}, + types::{BasicType, BasicTypeEnum, FunctionType, PointerType}, values::{BasicValue, FloatValue, FunctionValue, IntValue, PhiValue, PointerValue}, AddressSpace, FloatPredicate, IntPredicate, }; use smallvec::SmallVec; +use std::sync::Arc; use wasmer_runtime_core::{ + backend::{Backend, CacheGen, Token}, + cache::{Artifact, Error as CacheError}, + codegen::*, memory::MemoryType, - module::{ExportIndex, ModuleInfo}, - structures::{Map, SliceMap, TypedIndex}, + module::{ModuleInfo, ModuleInner}, + structures::{Map, TypedIndex}, types::{ - FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex, - TableIndex, Type, + FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, SigIndex, TableIndex, Type, }, }; -use wasmparser::{ - BinaryReaderError, CodeSectionReader, LocalsReader, MemoryImmediate, Operator, OperatorsReader, -}; +use wasmparser::{BinaryReaderError, MemoryImmediate, Operator, Type as WpType}; +use crate::backend::LLVMBackend; use crate::intrinsics::{CtxType, GlobalCache, Intrinsics, MemoryCache}; use crate::read_info::type_to_type; use crate::state::{ControlFrame, IfElseState, State}; @@ -58,144 +60,353 @@ fn type_to_llvm(intrinsics: &Intrinsics, ty: Type) -> BasicTypeEnum { } } -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, +fn trap_if_not_representatable_as_int( 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]; + context: &Context, + function: &FunctionValue, + lower_bounds: f64, + upper_bound: f64, + value: FloatValue, +) { + enum FloatSize { + Bits32, + Bits64, + } - let function = functions[func_index]; - let mut state = State::new(); - let entry_block = context.append_basic_block(&function, "entry"); + let failure_block = context.append_basic_block(function, "conversion_failure_block"); + let continue_block = context.append_basic_block(function, "conversion_success_block"); - let return_block = context.append_basic_block(&function, "return"); - builder.position_at_end(&return_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 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(); + 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() + }; - state.push_block(return_block, phis); - builder.position_at_end(&entry_block); + let (shift_amount, exponent_mask, invalid_exponent) = match float_size { + FloatSize::Bits32 => (23, 0b01111111100000000000000000000000, 0b11111111), + FloatSize::Bits64 => ( + 52, + 0b0111111111110000000000000000000000000000000000000000000000000000, + 0b11111111111, + ), + }; - let mut locals = Vec::with_capacity(locals_reader.get_count() as usize); // TODO fix capacity + builder.build_and( + float_bits, + int_ty.const_int(exponent_mask, false), + "masked_bits", + ); - locals.extend( - function - .get_param_iter() - .skip(1) - .enumerate() - .map(|(index, param)| { - let ty = param.get_type(); + ( + builder.build_right_shift( + float_bits, + int_ty.const_int(shift_amount, false), + false, + "exponent", + ), + invalid_exponent, + ) + }; - let alloca = builder.build_alloca(ty, &format!("local{}", index)); - builder.build_store(alloca, param); - alloca - }), + 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 param_len = locals.len(); + 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(); - let mut local_idx = 0; - for (index, local) in locals_reader.into_iter().enumerate() { - let (count, ty) = local?; + 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), intrinsics); + + 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())) +} + +#[derive(Debug)] +pub struct CodegenError { + pub message: String, +} + +pub struct LLVMModuleCodeGenerator { + context: Option, + builder: Option, + intrinsics: Option, + functions: Vec, + signatures: Map, + signatures_raw: Map, + function_signatures: Option>>, + func_import_count: usize, + personality_func: FunctionValue, + module: Module, +} + +pub struct LLVMFunctionCodeGenerator { + context: Option, + builder: Option, + intrinsics: Option, + state: State, + function: FunctionValue, + func_sig: FuncSig, + signatures: Map, + locals: Vec, // Contains params and locals + num_params: usize, + ctx: Option>, + unreachable_depth: usize, +} + +impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { + fn feed_return(&mut self, _ty: WpType) -> Result<(), CodegenError> { + Ok(()) + } + + fn feed_param(&mut self, _ty: WpType) -> Result<(), CodegenError> { + Ok(()) + } + + fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), CodegenError> { + let param_len = self.num_params; + + let mut local_idx = 0; + // let (count, ty) = local?; + let count = n; let wasmer_ty = type_to_type(ty)?; + + let intrinsics = self.intrinsics.as_ref().unwrap(); let ty = type_to_llvm(intrinsics, wasmer_ty); let default_value = match wasmer_ty { @@ -205,51 +416,90 @@ fn parse_function( Type::F64 => intrinsics.f64_zero.as_basic_value_enum(), }; + let builder = self.builder.as_ref().unwrap(); + 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); + self.locals.push(alloca); local_idx += 1; } + Ok(()) } - 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); + fn begin_body(&mut self, module_info: &ModuleInfo) -> Result<(), CodegenError> { + let start_of_code_block = self + .context + .as_ref() + .unwrap() + .append_basic_block(&self.function, "start_of_code"); + let entry_end_inst = self + .builder + .as_ref() + .unwrap() + .build_unconditional_branch(&start_of_code_block); + self.builder + .as_ref() + .unwrap() + .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; + let cache_builder = self.context.as_ref().unwrap().create_builder(); + cache_builder.position_before(&entry_end_inst); + let module_info = + unsafe { ::std::mem::transmute::<&ModuleInfo, &'static ModuleInfo>(module_info) }; + let function = unsafe { + ::std::mem::transmute::<&FunctionValue, &'static FunctionValue>(&self.function) + }; + let ctx = CtxType::new(module_info, function, cache_builder); + + self.ctx = Some(ctx); + Ok(()) + } + + fn feed_event(&mut self, event: Event, module_info: &ModuleInfo) -> Result<(), CodegenError> { + let op = match event { + Event::Wasm(x) => x, + Event::Internal(_x) => { + return Ok(()); + } + }; + + let mut state = &mut self.state; + let builder = self.builder.as_ref().unwrap(); + let context = self.context.as_ref().unwrap(); + let function = self.function; + let intrinsics = self.intrinsics.as_ref().unwrap(); + let locals = &self.locals; + let info = module_info; + let signatures = &self.signatures; + let mut ctx = self.ctx.as_mut().unwrap(); - for op in op_reader { - let op = op?; if !state.reachable { - match op { + match *op { Operator::Block { ty: _ } | Operator::Loop { ty: _ } | Operator::If { ty: _ } => { - unreachable_depth += 1; - continue; + self.unreachable_depth += 1; + return Ok(()); } Operator::Else => { - if unreachable_depth != 0 { - continue; + if self.unreachable_depth != 0 { + return Ok(()); } } Operator::End => { - if unreachable_depth != 0 { - unreachable_depth -= 1; - continue; + if self.unreachable_depth != 0 { + self.unreachable_depth -= 1; + return Ok(()); } } _ => { - continue; + return Ok(()); } } } - match op { + match *op { /*************************** * Control Flow instructions. * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#control-flow-instructions @@ -383,7 +633,12 @@ fn parse_function( .iter() .enumerate() .map(|(case_index, &depth)| { - let frame = state.frame_at_depth(depth)?; + let frame_result: Result<&ControlFrame, BinaryReaderError> = + state.frame_at_depth(depth); + let frame = match frame_result { + Ok(v) => v, + Err(e) => return Err(e), + }; let case_index_literal = context.i32_type().const_int(case_index as u64, false); @@ -397,7 +652,8 @@ fn parse_function( builder.build_switch(index.into_int_value(), default_frame.br_dest(), &cases[..]); - state.popn(args.len())?; + let args_len = args.len(); + state.popn(args_len)?; state.reachable = false; } Operator::If { ty } => { @@ -490,7 +746,6 @@ fn parse_function( if let ControlFrame::IfElse { if_else, next, - phis, if_else_state, .. } = &frame @@ -620,7 +875,7 @@ fn parse_function( Operator::GetGlobal { global_index } => { let index = GlobalIndex::new(global_index as usize); - let global_cache = ctx.global_cache(index); + let global_cache = ctx.global_cache(index, intrinsics); match global_cache { GlobalCache::Const { value } => { state.push1(value); @@ -634,7 +889,7 @@ fn parse_function( Operator::SetGlobal { global_index } => { let value = state.pop1()?; let index = GlobalIndex::new(global_index as usize); - let global_cache = ctx.global_cache(index); + let global_cache = ctx.global_cache(index, intrinsics); match global_cache { GlobalCache::Mut { ptr_to_value } => { builder.build_store(ptr_to_value, value); @@ -670,12 +925,14 @@ fn parse_function( .map(|v| *v) .collect(); - let func_ptr = ctx.local_func(local_func_index, llvm_sig); + let func_ptr = + ctx.local_func(local_func_index, llvm_sig, intrinsics, builder); 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 (func_ptr_untyped, ctx_ptr) = + ctx.imported_func(import_func_index, intrinsics); let params: Vec<_> = [ctx_ptr.as_basic_value_enum()] .iter() .chain(state.peekn(func_sig.params().len())?.iter()) @@ -714,8 +971,9 @@ fn parse_function( } 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 expected_dynamic_sigindex = ctx.dynamic_sigindex(sig_index, intrinsics); + let (table_base, table_bound) = + ctx.table(TableIndex::new(table_index as usize), intrinsics, builder); let func_index = state.pop1()?.into_int_value(); // We assume the table has the `anyfunc` element type. @@ -866,7 +1124,7 @@ fn parse_function( let value = call_site.try_as_basic_value().left().unwrap(); state.push1(value); } - returns @ _ => unimplemented!("multi-value returns"), + _ => unimplemented!("multi-value returns"), } } @@ -1732,7 +1990,7 @@ fn parse_function( * Load and Store instructions. * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#load-and-store-instructions ***************************/ - Operator::I32Load { memarg } => { + Operator::I32Load { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1746,7 +2004,7 @@ fn parse_function( let result = builder.build_load(effective_address, &state.var_name()); state.push1(result); } - Operator::I64Load { memarg } => { + Operator::I64Load { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1760,7 +2018,7 @@ fn parse_function( let result = builder.build_load(effective_address, &state.var_name()); state.push1(result); } - Operator::F32Load { memarg } => { + Operator::F32Load { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1774,7 +2032,7 @@ fn parse_function( let result = builder.build_load(effective_address, &state.var_name()); state.push1(result); } - Operator::F64Load { memarg } => { + Operator::F64Load { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1789,7 +2047,7 @@ fn parse_function( state.push1(result); } - Operator::I32Store { memarg } => { + Operator::I32Store { ref memarg } => { let value = state.pop1()?; let effective_address = resolve_memory_ptr( builder, @@ -1803,7 +2061,7 @@ fn parse_function( )?; builder.build_store(effective_address, value); } - Operator::I64Store { memarg } => { + Operator::I64Store { ref memarg } => { let value = state.pop1()?; let effective_address = resolve_memory_ptr( builder, @@ -1817,7 +2075,7 @@ fn parse_function( )?; builder.build_store(effective_address, value); } - Operator::F32Store { memarg } => { + Operator::F32Store { ref memarg } => { let value = state.pop1()?; let effective_address = resolve_memory_ptr( builder, @@ -1831,7 +2089,7 @@ fn parse_function( )?; builder.build_store(effective_address, value); } - Operator::F64Store { memarg } => { + Operator::F64Store { ref memarg } => { let value = state.pop1()?; let effective_address = resolve_memory_ptr( builder, @@ -1846,7 +2104,7 @@ fn parse_function( builder.build_store(effective_address, value); } - Operator::I32Load8S { memarg } => { + Operator::I32Load8S { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1864,7 +2122,7 @@ fn parse_function( builder.build_int_s_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); state.push1(result); } - Operator::I32Load16S { memarg } => { + Operator::I32Load16S { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1882,7 +2140,7 @@ fn parse_function( builder.build_int_s_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); state.push1(result); } - Operator::I64Load8S { memarg } => { + Operator::I64Load8S { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1900,7 +2158,7 @@ fn parse_function( builder.build_int_s_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); state.push1(result); } - Operator::I64Load16S { memarg } => { + Operator::I64Load16S { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1918,7 +2176,7 @@ fn parse_function( builder.build_int_s_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); state.push1(result); } - Operator::I64Load32S { memarg } => { + Operator::I64Load32S { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1937,7 +2195,7 @@ fn parse_function( state.push1(result); } - Operator::I32Load8U { memarg } => { + Operator::I32Load8U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1955,7 +2213,7 @@ fn parse_function( builder.build_int_z_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); state.push1(result); } - Operator::I32Load16U { memarg } => { + Operator::I32Load16U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1973,7 +2231,7 @@ fn parse_function( builder.build_int_z_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); state.push1(result); } - Operator::I64Load8U { memarg } => { + Operator::I64Load8U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1991,7 +2249,7 @@ fn parse_function( builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); state.push1(result); } - Operator::I64Load16U { memarg } => { + Operator::I64Load16U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -2009,7 +2267,7 @@ fn parse_function( builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); state.push1(result); } - Operator::I64Load32U { memarg } => { + Operator::I64Load32U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -2028,7 +2286,7 @@ fn parse_function( state.push1(result); } - Operator::I32Store8 { memarg } | Operator::I64Store8 { memarg } => { + Operator::I32Store8 { ref memarg } | Operator::I64Store8 { ref memarg } => { let value = state.pop1()?.into_int_value(); let effective_address = resolve_memory_ptr( builder, @@ -2044,7 +2302,7 @@ fn parse_function( builder.build_int_truncate(value, intrinsics.i8_ty, &state.var_name()); builder.build_store(effective_address, narrow_value); } - Operator::I32Store16 { memarg } | Operator::I64Store16 { memarg } => { + Operator::I32Store16 { ref memarg } | Operator::I64Store16 { ref memarg } => { let value = state.pop1()?.into_int_value(); let effective_address = resolve_memory_ptr( builder, @@ -2060,7 +2318,7 @@ fn parse_function( builder.build_int_truncate(value, intrinsics.i16_ty, &state.var_name()); builder.build_store(effective_address, narrow_value); } - Operator::I64Store32 { memarg } => { + Operator::I64Store32 { ref memarg } => { let value = state.pop1()?.into_int_value(); let effective_address = resolve_memory_ptr( builder, @@ -2143,324 +2401,239 @@ fn parse_function( ); state.push1(result.try_as_basic_value().left().unwrap()); } - op @ _ => { + _ => { unimplemented!("{:?}", op); } } + + Ok(()) } - let results = state.popn_save(func_sig.returns().len())?; + fn finalize(&mut self) -> Result<(), CodegenError> { + let results = self.state.popn_save(self.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") + match results.as_slice() { + [] => { + self.builder.as_ref().unwrap().build_return(None); + } + [one_value] => { + self.builder.as_ref().unwrap().build_return(Some(one_value)); + } + _ => unimplemented!("multi-value returns not yet implemented"), } + Ok(()) } - - 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, +impl From for CodegenError { + fn from(other: BinaryReaderError) -> CodegenError { + CodegenError { + message: format!("{:?}", other), + } } +} - let failure_block = context.append_basic_block(function, "conversion_failure_block"); - let continue_block = context.append_basic_block(function, "conversion_success_block"); +impl ModuleCodeGenerator + for LLVMModuleCodeGenerator +{ + fn new() -> LLVMModuleCodeGenerator { + let context = Context::create(); + let module = context.create_module("module"); + let builder = context.create_builder(); - 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 intrinsics = Intrinsics::declare(&module, &context); - 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", + let personality_func = module.add_function( + "__gxx_personality_v0", + intrinsics.i32_ty.fn_type(&[], false), + Some(Linkage::External), ); - ( - builder.build_right_shift( - float_bits, - int_ty.const_int(shift_amount, false), - false, - "exponent", - ), - invalid_exponent, - ) - }; + let signatures = Map::new(); - 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 + LLVMModuleCodeGenerator { + context: Some(context), + builder: Some(builder), + intrinsics: Some(intrinsics), + module, + functions: vec![], + signatures, + signatures_raw: Map::new(), + function_signatures: None, + func_import_count: 0, + personality_func, } - 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())) + fn backend_id() -> Backend { + Backend::LLVM + } + + fn check_precondition(&mut self, _module_info: &ModuleInfo) -> Result<(), CodegenError> { + Ok(()) + } + + fn next_function(&mut self) -> Result<&mut LLVMFunctionCodeGenerator, CodegenError> { + // Creates a new function and returns the function-scope code generator for it. + let (context, builder, intrinsics) = match self.functions.last_mut() { + Some(x) => ( + x.context.take().unwrap(), + x.builder.take().unwrap(), + x.intrinsics.take().unwrap(), + ), + None => ( + self.context.take().unwrap(), + self.builder.take().unwrap(), + self.intrinsics.take().unwrap(), + ), + }; + + let sig_id = self.function_signatures.as_ref().unwrap() + [FuncIndex::new(self.func_import_count + self.functions.len())]; + let func_sig = self.signatures_raw[sig_id].clone(); + + let function = self.module.add_function( + &format!("fn{}", self.func_import_count + self.functions.len()), + self.signatures[sig_id], + Some(Linkage::External), + ); + function.set_personality_function(self.personality_func); + + 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::new(); + 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 num_params = locals.len(); + + let code = LLVMFunctionCodeGenerator { + state, + context: Some(context), + builder: Some(builder), + intrinsics: Some(intrinsics), + function, + func_sig: func_sig, + locals, + signatures: self.signatures.clone(), + num_params, + ctx: None, + unreachable_depth: 0, + }; + self.functions.push(code); + Ok(self.functions.last_mut().unwrap()) + } + + fn finalize( + mut self, + module_info: &ModuleInfo, + ) -> Result<(LLVMBackend, Box), CodegenError> { + let (context, builder, intrinsics) = match self.functions.last_mut() { + Some(x) => ( + x.context.take().unwrap(), + x.builder.take().unwrap(), + x.intrinsics.take().unwrap(), + ), + None => ( + self.context.take().unwrap(), + self.builder.take().unwrap(), + self.intrinsics.take().unwrap(), + ), + }; + self.context = Some(context); + self.builder = Some(builder); + self.intrinsics = Some(intrinsics); + + generate_trampolines( + module_info, + &self.signatures, + &self.module, + self.context.as_ref().unwrap(), + self.builder.as_ref().unwrap(), + self.intrinsics.as_ref().unwrap(), + ); + + let pass_manager = PassManager::create_for_module(); + if cfg!(test) { + 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_aggressive_inst_combiner_pass(); + pass_manager.add_merged_load_store_motion_pass(); + pass_manager.add_new_gvn_pass(); + pass_manager.add_aggressive_dce_pass(); + pass_manager.run_on_module(&self.module); + + // self.module.print_to_stderr(); + + let (backend, cache_gen) = LLVMBackend::new(self.module, self.intrinsics.take().unwrap()); + Ok((backend, Box::new(cache_gen))) + } + + fn feed_signatures(&mut self, signatures: Map) -> Result<(), CodegenError> { + self.signatures = signatures + .iter() + .map(|(_, sig)| { + func_sig_to_llvm( + self.context.as_ref().unwrap(), + self.intrinsics.as_ref().unwrap(), + sig, + ) + }) + .collect(); + self.signatures_raw = signatures.clone(); + Ok(()) + } + + fn feed_function_signatures( + &mut self, + assoc: Map, + ) -> Result<(), CodegenError> { + self.function_signatures = Some(Arc::new(assoc)); + Ok(()) + } + + fn feed_import_function(&mut self) -> Result<(), CodegenError> { + self.func_import_count += 1; + Ok(()) + } + + unsafe fn from_cache(artifact: Artifact, _: Token) -> Result { + let (info, _, memory) = artifact.consume(); + let (backend, cache_gen) = + LLVMBackend::from_buffer(memory).map_err(CacheError::DeserializeError)?; + + Ok(ModuleInner { + runnable_module: Box::new(backend), + cache_gen: Box::new(cache_gen), + + info, + }) + } } diff --git a/lib/llvm-backend/src/example.rs b/lib/llvm-backend/src/example.rs deleted file mode 100644 index 5ce3c9a5f..000000000 --- a/lib/llvm-backend/src/example.rs +++ /dev/null @@ -1,61 +0,0 @@ -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 index ba1e3abd3..6250fd4fd 100644 --- a/lib/llvm-backend/src/intrinsics.rs +++ b/lib/llvm-backend/src/intrinsics.rs @@ -4,10 +4,7 @@ use inkwell::{ context::Context, module::Module, types::{BasicType, FloatType, FunctionType, IntType, PointerType, StructType, VoidType}, - values::{ - BasicValue, BasicValueEnum, FloatValue, FunctionValue, InstructionValue, IntValue, - PointerValue, - }, + values::{BasicValue, BasicValueEnum, FloatValue, FunctionValue, IntValue, PointerValue}, AddressSpace, }; use std::marker::PhantomData; @@ -117,7 +114,6 @@ pub struct Intrinsics { pub throw_trap: FunctionValue, - ctx_ty: StructType, pub ctx_ptr_ty: PointerType, } @@ -370,38 +366,9 @@ impl Intrinsics { 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)] @@ -435,13 +402,8 @@ struct ImportedFuncCache { } 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, @@ -455,17 +417,36 @@ pub struct CtxType<'a> { } impl<'a> CtxType<'a> { + pub fn new( + info: &'a ModuleInfo, + func_value: &'a FunctionValue, + cache_builder: Builder, + ) -> CtxType<'a> { + CtxType { + ctx_ptr_value: func_value.get_nth_param(0).unwrap().into_pointer_value(), + + 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, + } + } + 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) = ( + pub fn memory(&mut self, index: MemoryIndex, intrinsics: &Intrinsics) -> MemoryCache { + let (cached_memories, info, ctx_ptr_value, cache_builder) = ( &mut self.cached_memories, - self.builder, self.info, self.ctx_ptr_value, - self.intrinsics, &self.cache_builder, ); @@ -526,13 +507,16 @@ impl<'a> CtxType<'a> { }) } - pub fn table(&mut self, index: TableIndex) -> (PointerValue, IntValue) { - let (cached_tables, builder, info, ctx_ptr_value, intrinsics, cache_builder) = ( + pub fn table( + &mut self, + index: TableIndex, + intrinsics: &Intrinsics, + builder: &Builder, + ) -> (PointerValue, IntValue) { + let (cached_tables, info, ctx_ptr_value, cache_builder) = ( &mut self.cached_tables, - self.builder, self.info, self.ctx_ptr_value, - self.intrinsics, &self.cache_builder, ); @@ -587,43 +571,39 @@ impl<'a> CtxType<'a> { ) } - 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 + pub fn local_func( + &mut self, + index: LocalFuncIndex, + fn_ty: FunctionType, + intrinsics: &Intrinsics, + builder: &Builder, + ) -> PointerValue { + let local_func_array_ptr_ptr = + unsafe { builder.build_struct_gep(self.ctx_ptr_value, 8, "local_func_array_ptr_ptr") }; + let local_func_array_ptr = 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( + builder.build_in_bounds_gep( local_func_array_ptr, - &[self - .intrinsics - .i32_ty - .const_int(index.index() as u64, false)], + &[intrinsics.i32_ty.const_int(index.index() as u64, false)], "local_func_ptr_ptr", ) }; - let local_func_ptr = self - .builder + let local_func_ptr = builder .build_load(local_func_ptr_ptr, "local_func_ptr") .into_pointer_value(); - self.builder.build_pointer_cast( + 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) = ( + pub fn dynamic_sigindex(&mut self, index: SigIndex, intrinsics: &Intrinsics) -> IntValue { + let (cached_sigindices, ctx_ptr_value, cache_builder) = ( &mut self.cached_sigindices, - self.builder, - self.info, self.ctx_ptr_value, - self.intrinsics, &self.cache_builder, ); @@ -650,13 +630,11 @@ impl<'a> CtxType<'a> { }) } - pub fn global_cache(&mut self, index: GlobalIndex) -> GlobalCache { - let (cached_globals, builder, ctx_ptr_value, info, intrinsics, cache_builder) = ( + pub fn global_cache(&mut self, index: GlobalIndex, intrinsics: &Intrinsics) -> GlobalCache { + let (cached_globals, ctx_ptr_value, info, cache_builder) = ( &mut self.cached_globals, - self.builder, self.ctx_ptr_value, self.info, - self.intrinsics, &self.cache_builder, ); @@ -727,12 +705,14 @@ impl<'a> CtxType<'a> { }) } - pub fn imported_func(&mut self, index: ImportedFuncIndex) -> (PointerValue, PointerValue) { - let (cached_imported_functions, builder, ctx_ptr_value, intrinsics, cache_builder) = ( + pub fn imported_func( + &mut self, + index: ImportedFuncIndex, + intrinsics: &Intrinsics, + ) -> (PointerValue, PointerValue) { + let (cached_imported_functions, ctx_ptr_value, cache_builder) = ( &mut self.cached_imported_functions, - self.builder, self.ctx_ptr_value, - self.intrinsics, &self.cache_builder, ); @@ -770,38 +750,4 @@ impl<'a> CtxType<'a> { (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 index 85bcc7e19..6d8816af8 100644 --- a/lib/llvm-backend/src/lib.rs +++ b/lib/llvm-backend/src/lib.rs @@ -1,18 +1,6 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] #![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; @@ -21,119 +9,11 @@ mod read_info; mod state; mod trampolines; -pub struct LLVMCompiler { - _private: (), -} +use wasmer_runtime_core::codegen::SimpleStreamingCompilerGen; -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); - } -} +pub type LLVMCompiler = SimpleStreamingCompilerGen< + code::LLVMModuleCodeGenerator, + code::LLVMFunctionCodeGenerator, + backend::LLVMBackend, + code::CodegenError, +>; diff --git a/lib/llvm-backend/src/platform/unix.rs b/lib/llvm-backend/src/platform/unix.rs index 2267ab129..d16384179 100644 --- a/lib/llvm-backend/src/platform/unix.rs +++ b/lib/llvm-backend/src/platform/unix.rs @@ -1,7 +1,5 @@ use libc::{c_void, siginfo_t}; -use nix::sys::signal::{ - sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV, -}; +use nix::sys::signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet, SIGBUS, SIGSEGV}; /// `__register_frame` and `__deregister_frame` on macos take a single fde as an /// argument, so we need to parse the fde table here. @@ -36,7 +34,7 @@ pub unsafe fn visit_fde(addr: *mut u8, size: usize, visitor: extern "C" fn(*mut } #[cfg(not(target_os = "macos"))] -pub unsafe fn visit_fde(addr: *mut u8, size: usize, visitor: extern "C" fn(*mut u8)) { +pub unsafe fn visit_fde(addr: *mut u8, _size: usize, visitor: extern "C" fn(*mut u8)) { visitor(addr); } @@ -51,24 +49,22 @@ pub unsafe fn install_signal_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, + _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. + // 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 index a974beb59..572469ce2 100644 --- a/lib/llvm-backend/src/read_info.rs +++ b/lib/llvm-backend/src/read_info.rs @@ -1,280 +1,5 @@ -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 { .. } => {} - } - } -} +use wasmer_runtime_core::types::Type; +use wasmparser::{BinaryReaderError, Type as WpType}; pub fn type_to_type(ty: WpType) -> Result { Ok(match ty { @@ -296,46 +21,3 @@ pub fn type_to_type(ty: WpType) -> Result { } }) } - -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/trampolines.rs b/lib/llvm-backend/src/trampolines.rs index 4619cb990..23677618b 100644 --- a/lib/llvm-backend/src/trampolines.rs +++ b/lib/llvm-backend/src/trampolines.rs @@ -3,10 +3,9 @@ use inkwell::{ builder::Builder, context::Context, module::{Linkage, Module}, - passes::PassManager, - types::{BasicType, BasicTypeEnum, FunctionType, PointerType}, - values::{BasicValue, FunctionValue, PhiValue, PointerValue}, - AddressSpace, FloatPredicate, IntPredicate, + types::{BasicType, FunctionType}, + values::FunctionValue, + AddressSpace, }; use wasmer_runtime_core::{ module::ModuleInfo, @@ -43,20 +42,12 @@ pub fn generate_trampolines( Some(Linkage::External), ); - generate_trampoline( - trampoline_func, - func_type, - sig, - context, - builder, - intrinsics, - ); + generate_trampoline(trampoline_func, sig, context, builder, intrinsics); } } fn generate_trampoline( trampoline_func: FunctionValue, - sig_type: FunctionType, func_sig: &FuncSig, context: &Context, builder: &Builder, diff --git a/lib/middleware-common/Cargo.toml b/lib/middleware-common/Cargo.toml new file mode 100644 index 000000000..57a42577c --- /dev/null +++ b/lib/middleware-common/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "wasmer-middleware-common" +version = "0.4.1" +repository = "https://github.com/wasmerio/wasmer" +description = "Wasmer runtime common middlewares" +license = "MIT" +authors = ["The Wasmer Engineering Team "] +edition = "2018" + +[dependencies] +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } diff --git a/lib/middleware-common/src/call_trace.rs b/lib/middleware-common/src/call_trace.rs new file mode 100644 index 000000000..9c47d7d7b --- /dev/null +++ b/lib/middleware-common/src/call_trace.rs @@ -0,0 +1,27 @@ +use wasmer_runtime_core::{ + codegen::{Event, EventSink, FunctionMiddleware, InternalEvent}, + module::ModuleInfo, +}; + +pub struct CallTrace; + +impl FunctionMiddleware for CallTrace { + type Error = String; + fn feed_event<'a, 'b: 'a>( + &mut self, + op: Event<'a, 'b>, + _module_info: &ModuleInfo, + sink: &mut EventSink<'a, 'b>, + ) -> Result<(), Self::Error> { + match op { + Event::Internal(InternalEvent::FunctionBegin(id)) => sink.push(Event::Internal( + InternalEvent::Breakpoint(Box::new(move |_| { + eprintln!("func ({})", id); + })), + )), + _ => {} + } + sink.push(op); + Ok(()) + } +} diff --git a/lib/middleware-common/src/lib.rs b/lib/middleware-common/src/lib.rs new file mode 100644 index 000000000..e09d9ee03 --- /dev/null +++ b/lib/middleware-common/src/lib.rs @@ -0,0 +1,3 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + +pub mod call_trace; diff --git a/lib/runtime-abi/Cargo.toml b/lib/runtime-abi/Cargo.toml new file mode 100644 index 000000000..f8b540e4b --- /dev/null +++ b/lib/runtime-abi/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "wasmer-runtime-abi" +version = "0.4.1" +description = "Wasmer runtime core library" +license = "MIT" +authors = ["The Wasmer Engineering Team "] +repository = "https://github.com/wasmerio/wasmer" +edition = "2018" + +[dependencies] +libc = "0.2.50" +wasmer-runtime-core = { path = "../runtime-core" } +hashbrown = "0.1" +failure = "0.1" +tar = "0.4" +wasmparser = "0.29.2" +zstd = "0.4" + +[target.'cfg(unix)'.dependencies.zbox] +git = "https://github.com/wasmerio/zbox" +branch = "bundle-libsodium" +features = ["libsodium-bundled"] + +[dev-dependencies] +tempdir = "0.3" + +[features] +debug = [] diff --git a/lib/runtime-abi/README.md b/lib/runtime-abi/README.md new file mode 100644 index 000000000..fe906c2dd --- /dev/null +++ b/lib/runtime-abi/README.md @@ -0,0 +1,23 @@ +# runtime-abi + +This crate has ABI functions (like syscalls) and extensions to the runtime for enabling ABIs (e.g. virtual filesystem). + +## Virtual Filesystem (experimental) + +The virtual filesystem allows the runtime to read bundled wasm data as if they were files. Data that is stored in a +custom section compressed with [zstd][1] compression and archived with [tar][2] will be exposed as files and mounted +in the `/` root. + +The only current supported operation is the `read` syscall. + +The virtual filesystem is not enabled by default. Build with `--features vfs` to use it. + +[Zbox][3] is a virtual filesystem that depends on [libsodium][4]. See [installation instructions][5] for libsodium here. One can +statically link libsodium with the [instructions][6] on Zbox's readme. + +[1]: https://facebook.github.io/zstd/ +[2]: https://www.gnu.org/software/tar/ +[3]: https://zbox.io/ +[4]: https://download.libsodium.org/doc/ +[5]: https://download.libsodium.org/doc/installation +[6]: https://github.com/zboxfs/zbox#static-linking-with-libsodium diff --git a/lib/runtime-abi/src/lib.rs b/lib/runtime-abi/src/lib.rs new file mode 100644 index 000000000..d1c9e2326 --- /dev/null +++ b/lib/runtime-abi/src/lib.rs @@ -0,0 +1,8 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + +#[cfg(not(target_os = "windows"))] +#[macro_use] +extern crate failure; + +#[cfg(not(target_os = "windows"))] +pub mod vfs; diff --git a/lib/runtime-abi/src/vfs/device_file.rs b/lib/runtime-abi/src/vfs/device_file.rs new file mode 100644 index 000000000..3013d32b2 --- /dev/null +++ b/lib/runtime-abi/src/vfs/device_file.rs @@ -0,0 +1,111 @@ +use crate::vfs::file_like::{FileLike, Metadata}; +use failure::Error; +use std::io; + +pub struct Stdin; +pub struct Stdout; +pub struct Stderr; + +impl FileLike for Stdin { + fn metadata(&self) -> Result { + unimplemented!() + } + + fn set_file_len(&mut self, _len: usize) -> Result<(), failure::Error> { + panic!("Cannot set length of stdin"); + } +} + +impl io::Read for Stdin { + fn read(&mut self, _buf: &mut [u8]) -> Result { + unimplemented!() + } +} + +impl io::Write for Stdin { + fn write(&mut self, _buf: &[u8]) -> Result { + unimplemented!() + } + + fn flush(&mut self) -> Result<(), io::Error> { + unimplemented!() + } +} + +impl io::Seek for Stdin { + fn seek(&mut self, _pos: io::SeekFrom) -> Result { + unimplemented!() + } +} + +impl FileLike for Stdout { + fn metadata(&self) -> Result { + unimplemented!() + } + + fn set_file_len(&mut self, _len: usize) -> Result<(), failure::Error> { + panic!("Cannot set length of stdout"); + } +} + +impl io::Read for Stdout { + fn read(&mut self, _buf: &mut [u8]) -> Result { + unimplemented!() + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> Result { + let stdout = io::stdout(); + let mut handle = stdout.lock(); + handle.write(buf) + } + + fn flush(&mut self) -> Result<(), io::Error> { + let stdout = io::stdout(); + let mut handle = stdout.lock(); + handle.flush() + } +} + +impl io::Seek for Stdout { + fn seek(&mut self, _pos: io::SeekFrom) -> Result { + unimplemented!() + } +} + +impl FileLike for Stderr { + fn metadata(&self) -> Result { + unimplemented!() + } + + fn set_file_len(&mut self, _len: usize) -> Result<(), failure::Error> { + panic!("Cannot set length of stderr"); + } +} + +impl io::Read for Stderr { + fn read(&mut self, _buf: &mut [u8]) -> Result { + unimplemented!() + } +} + +impl io::Write for Stderr { + fn write(&mut self, buf: &[u8]) -> Result { + let stderr = io::stderr(); + let mut handle = stderr.lock(); + handle.write(buf) + } + + fn flush(&mut self) -> Result<(), io::Error> { + let stderr = io::stderr(); + let mut handle = stderr.lock(); + handle.flush() + } +} + +impl io::Seek for Stderr { + fn seek(&mut self, _pos: io::SeekFrom) -> Result { + unimplemented!() + } +} diff --git a/lib/runtime-abi/src/vfs/file_like.rs b/lib/runtime-abi/src/vfs/file_like.rs new file mode 100644 index 000000000..4b9b8771c --- /dev/null +++ b/lib/runtime-abi/src/vfs/file_like.rs @@ -0,0 +1,21 @@ +pub type Fd = isize; + +#[derive(Debug)] +pub struct Metadata { + pub len: usize, + pub is_file: bool, +} + +pub trait FileLike: std::io::Write + std::io::Read + std::io::Seek { + // get metadata + fn metadata(&self) -> Result; + + // write + // fn write_file(&mut self, buf: &[u8]) -> Result; + + // read + // fn read_file(&mut self, buf: &mut [u8]) -> Result; + + // set_file_len + fn set_file_len(&mut self, len: usize) -> Result<(), failure::Error>; +} diff --git a/lib/runtime-abi/src/vfs/mod.rs b/lib/runtime-abi/src/vfs/mod.rs new file mode 100644 index 000000000..0a152b5b3 --- /dev/null +++ b/lib/runtime-abi/src/vfs/mod.rs @@ -0,0 +1,5 @@ +pub mod device_file; +pub mod file_like; +pub mod vfs; +pub mod vfs_header; +pub mod virtual_file; diff --git a/lib/runtime-abi/src/vfs/vfs.rs b/lib/runtime-abi/src/vfs/vfs.rs new file mode 100644 index 000000000..5cb7a0997 --- /dev/null +++ b/lib/runtime-abi/src/vfs/vfs.rs @@ -0,0 +1,170 @@ +use crate::vfs::file_like::FileLike; +use crate::vfs::vfs_header::{header_from_bytes, ArchiveType, CompressionType}; +use crate::vfs::virtual_file::VirtualFile; +use hashbrown::HashMap; +use std::cell::RefCell; +use std::io; +use std::io::Read; +use std::path::{Path, PathBuf}; +use std::rc::Rc; +use tar::EntryType; +use zbox::{init_env, OpenOptions, Repo, RepoOpener}; + +pub struct Vfs { + repo: Repo, + device_files: HashMap>>, +} + +impl Vfs { + /// Like `VfsBacking::from_tar_bytes` except it also decompresses from the zstd format. + pub fn from_tar_zstd_bytes(tar_bytes: Reader) -> Result { + let result = zstd::decode_all(tar_bytes); + let decompressed_data = result.unwrap(); + Self::from_tar_bytes(&decompressed_data[..]) + } + + /// Match on the type of the compressed-archive and select the correct unpack method + pub fn from_compressed_bytes(compressed_data_slice: &[u8]) -> Result { + let data_bytes = &compressed_data_slice[4..]; + match header_from_bytes(compressed_data_slice)? { + (_, CompressionType::ZSTD, ArchiveType::TAR) => Self::from_tar_zstd_bytes(data_bytes), + (_, CompressionType::NONE, ArchiveType::TAR) => Self::from_tar_bytes(data_bytes), + } + } + + /// Create a vfs from raw bytes in tar format + pub fn from_tar_bytes(tar_bytes: Reader) -> Result { + init_env(); + let mut repo = RepoOpener::new() + .create(true) + .open("mem://wasmer_fs", "") + .unwrap(); + let _errors = tar::Archive::new(tar_bytes) + .entries()? + .map(|entry| { + let mut entry: tar::Entry = entry?; + let path = entry.path()?; + let path = convert_to_absolute_path(path); + let _result = match (entry.header().entry_type(), path.parent()) { + (EntryType::Regular, Some(parent)) => { + if let Err(e) = repo.create_dir_all(parent) { + if e == zbox::Error::AlreadyExists || e == zbox::Error::IsRoot { + } else { + return Err(VfsAggregateError::ZboxError(e)); + } + } else { + } + let mut file = repo.create_file(&path)?; + if entry.header().size().unwrap_or(0) > 0 { + io::copy(&mut entry, &mut file)?; + file.finish()?; + } + } + (EntryType::Directory, _) => { + if let Err(e) = repo.create_dir_all(path) { + if e == zbox::Error::AlreadyExists || e == zbox::Error::IsRoot { + } else { + return Err(VfsAggregateError::ZboxError(e)); + } + } else { + } + } + _ => return Err(VfsAggregateError::UnsupportedFileType), + }; + Ok(()) + }) + .collect::>>(); + + // let import_errors = errors.iter().filter_map(|e| e.err()).collect::>(); + + let vfs = Self { + repo, + device_files: HashMap::new(), + // import_errors: vec![], + }; + Ok(vfs) + } + + pub fn new() -> Result<(Self, Vec), failure::Error> { + init_env(); + let repo = RepoOpener::new() + .create(true) + .open("mem://wasmer_fs", "") + .unwrap(); + Ok(( + Vfs { + repo, + device_files: HashMap::new(), + }, + vec![], + )) + } + + pub fn open_file>(&mut self, path: P) -> Option>> { + init_env(); + let path = convert_to_absolute_path(path); + if let Ok(file) = OpenOptions::new().write(true).open(&mut self.repo, &path) { + Some(Rc::new(RefCell::new(VirtualFile::new(file)))) + } else if let Some(dev_file) = self.device_files.get(&path) { + Some(dev_file.clone()) + } else { + None + } + } + + pub fn make_dir>(&mut self, path: P) { + self.repo.create_dir_all(path).unwrap(); + } + + pub fn create_device_file>(&mut self, path: P, file: Rc>) { + self.device_files.insert(path.as_ref().to_path_buf(), file); + } +} + +fn convert_to_absolute_path>(path: P) -> PathBuf { + let path = path.as_ref(); + if path.is_relative() { + std::path::PathBuf::from("/").join(path) + } else { + path.to_path_buf() + } +} + +pub type Handle = i32; +#[derive(Debug, Fail)] +pub enum VfsError { + #[fail(display = "File with file descriptor \"{}\" does not exist.", _0)] + FileWithFileDescriptorNotExist(Handle), + #[fail(display = "File descriptor does not exist.")] + FileDescriptorNotExist(Handle), + #[fail(display = "Source file descriptor does not exist.")] + SourceFileDescriptorDoesNotExist, + #[fail(display = "Target file descriptor already exists.")] + TargetFileDescriptorAlreadyExists, + #[fail(display = "Could not get a mutable reference to the file because it is in use.")] + CouldNotGetMutableReferenceToFile, +} + +#[derive(Debug, Fail)] +pub enum VfsAggregateError { + #[fail(display = "Entry error.")] + EntryError(std::io::Error), + #[fail(display = "IO error.")] + IoError(std::io::Error), + #[fail(display = "Zbox error.")] + ZboxError(zbox::Error), + #[fail(display = "Unsupported file type.")] + UnsupportedFileType, +} + +impl std::convert::From for VfsAggregateError { + fn from(error: std::io::Error) -> VfsAggregateError { + VfsAggregateError::EntryError(error) + } +} + +impl std::convert::From for VfsAggregateError { + fn from(error: zbox::Error) -> VfsAggregateError { + VfsAggregateError::ZboxError(error) + } +} diff --git a/lib/runtime-abi/src/vfs/vfs_header.rs b/lib/runtime-abi/src/vfs/vfs_header.rs new file mode 100644 index 000000000..c90c658b0 --- /dev/null +++ b/lib/runtime-abi/src/vfs/vfs_header.rs @@ -0,0 +1,57 @@ +/// Represents the version of this header schema. +#[repr(u8)] +#[derive(Debug, PartialEq)] +pub enum HeaderVersion { + Version1 = 1, +} + +/// Represents the compression type of the file data. Only Zstd or no-compression is supported. +#[repr(u8)] +#[derive(Debug, PartialEq)] +pub enum CompressionType { + NONE = 0, + ZSTD = 1, +} + +/// Represents the type of archive. The only supported archive is the Tar format. +#[repr(u8)] +#[derive(Debug, PartialEq)] +pub enum ArchiveType { + TAR = 0, +} + +// extract the header data from bytes +pub fn header_from_bytes( + bytes: &[u8], +) -> Result<(HeaderVersion, CompressionType, ArchiveType), HeaderError> { + if let Some(bytes) = bytes.get(..4) { + let version = match bytes[0] { + 1 => HeaderVersion::Version1, + x => return Err(HeaderError::UnknownHeaderVersion(x)), + }; + let compression_type = match bytes[1] { + 0 => CompressionType::NONE, + 1 => CompressionType::ZSTD, + x => return Err(HeaderError::UnknownCompressionType(x)), + }; + let archive_type = match bytes[2] { + 0 => ArchiveType::TAR, + x => return Err(HeaderError::UnknownArchiveType(x)), + }; + Ok((version, compression_type, archive_type)) + } else { + Err(HeaderError::HeaderTooSmall) + } +} + +#[derive(Debug, Fail)] +pub enum HeaderError { + #[fail(display = "The version is not supported: \"{}\"", _0)] + UnknownHeaderVersion(u8), + #[fail(display = "The compression type is unknown: \"{}\"", _0)] + UnknownCompressionType(u8), + #[fail(display = "The archive type is unknown: \"{}\"", _0)] + UnknownArchiveType(u8), + #[fail(display = "The header is too small.")] + HeaderTooSmall, +} diff --git a/lib/runtime-abi/src/vfs/virtual_file.rs b/lib/runtime-abi/src/vfs/virtual_file.rs new file mode 100644 index 000000000..cd72231a1 --- /dev/null +++ b/lib/runtime-abi/src/vfs/virtual_file.rs @@ -0,0 +1,51 @@ +use crate::vfs::file_like::{FileLike, Metadata}; +use failure::Error; +use std::io; + +pub struct VirtualFile(zbox::File); + +impl VirtualFile { + pub fn new(file: zbox::File) -> Self { + VirtualFile(file) + } +} + +impl FileLike for VirtualFile { + fn metadata(&self) -> Result { + self.0 + .metadata() + .map(|m| Metadata { + len: m.content_len(), + is_file: m.is_file(), + }) + .map_err(|e: zbox::Error| e.into()) + } + + fn set_file_len(&mut self, len: usize) -> Result<(), failure::Error> { + self.0.set_len(len).map_err(|e| e.into()) + } +} + +impl io::Write for VirtualFile { + fn write(&mut self, buf: &[u8]) -> Result { + let result = self.0.write(buf)?; + self.0.finish().unwrap(); + Ok(result) + } + + fn flush(&mut self) -> Result<(), io::Error> { + self.0.flush() + } +} + +impl io::Read for VirtualFile { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.0.read(buf) + } +} + +impl io::Seek for VirtualFile { + fn seek(&mut self, pos: io::SeekFrom) -> Result { + self.0.seek(pos) + } +} diff --git a/lib/runtime-c-api/Cargo.toml b/lib/runtime-c-api/Cargo.toml index 201c400a0..de93c42a5 100644 --- a/lib/runtime-c-api/Cargo.toml +++ b/lib/runtime-c-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime-c-api" -version = "0.2.1" +version = "0.4.1" description = "Wasmer C API library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -8,13 +8,23 @@ 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"] +[dependencies] +libc = "0.2" + +[dependencies.wasmer-runtime] +path = "../runtime" +version = "0.4.1" + +[dependencies.wasmer-runtime-core] +path = "../runtime-core" +version = "0.4.1" + +[features] +debug = ["wasmer-runtime/debug"] +llvm = ["wasmer-runtime/llvm"] + [build-dependencies] -cbindgen = "0.8" \ No newline at end of file +cbindgen = "0.8" diff --git a/lib/runtime-c-api/src/error.rs b/lib/runtime-c-api/src/error.rs new file mode 100644 index 000000000..d5fcfe354 --- /dev/null +++ b/lib/runtime-c-api/src/error.rs @@ -0,0 +1,102 @@ +//! Errors. + +use libc::{c_char, c_int}; +use std::cell::RefCell; +use std::error::Error; +use std::fmt::{self, Display, Formatter}; +use std::ptr; +use std::slice; + +thread_local! { + static LAST_ERROR: RefCell>> = RefCell::new(None); +} + +pub(crate) 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. +pub(crate) 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 error_message = match take_last_error() { + Some(err) => err.to_string(), + None => return 0, + }; + + let length = length as usize; + + if error_message.len() >= length { + // buffer is too small to hold the error message + return -1; + } + + let buffer = slice::from_raw_parts_mut(buffer as *mut u8, length); + + 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 + 1 +} + +#[derive(Debug)] +pub(crate) struct CApiError { + pub(crate) msg: String, +} + +impl Display for CApiError { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}", &self.msg) + } +} + +impl Error for CApiError {} diff --git a/lib/runtime-c-api/src/export.rs b/lib/runtime-c-api/src/export.rs new file mode 100644 index 000000000..6bcae32f2 --- /dev/null +++ b/lib/runtime-c-api/src/export.rs @@ -0,0 +1,440 @@ +//! Wasm exports. + +use crate::{ + error::{update_last_error, CApiError}, + global::wasmer_global_t, + import::wasmer_import_func_t, + memory::wasmer_memory_t, + module::wasmer_module_t, + table::wasmer_table_t, + value::{wasmer_value, wasmer_value_t, wasmer_value_tag}, + wasmer_byte_array, wasmer_result_t, +}; +use libc::{c_int, uint32_t}; +use std::{ptr, slice}; +use wasmer_runtime::{Instance, Memory, Module, Value}; +use wasmer_runtime_core::{export::Export, module::ExportIndex}; + +#[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_func_t; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_export_descriptor_t; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_export_descriptors_t; + +#[repr(C)] +#[derive(Clone, Copy)] +pub union wasmer_import_export_value { + pub func: *const wasmer_import_func_t, + pub table: *const wasmer_table_t, + pub memory: *const wasmer_memory_t, + pub global: *const wasmer_global_t, +} + +#[allow(non_camel_case_types)] +#[repr(u32)] +#[derive(Clone)] +pub enum wasmer_import_export_kind { + WASM_FUNCTION, + WASM_GLOBAL, + WASM_MEMORY, + WASM_TABLE, +} + +/// 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 extern "C" fn wasmer_export_descriptors_destroy( + export_descriptors: *mut wasmer_export_descriptors_t, +) { + if !export_descriptors.is_null() { + unsafe { 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() +} + +pub(crate) struct NamedExports(pub Vec); + +/// Frees the memory for the given exports +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_exports_destroy(exports: *mut wasmer_exports_t) { + if !exports.is_null() { + unsafe { Box::from_raw(exports as *mut NamedExports) }; + } +} + +/// Gets the length of the exports +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_exports_len(exports: *mut wasmer_exports_t) -> c_int { + if exports.is_null() { + return 0; + } + (*(exports as *mut NamedExports)).0.len() as c_int +} + +/// Gets wasmer_export by index +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_exports_get( + exports: *mut wasmer_exports_t, + idx: c_int, +) -> *mut wasmer_export_t { + if exports.is_null() { + return ptr::null_mut(); + } + let 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 + } +} + +/// 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 a memory pointer from an export 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. +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_export_to_memory( + export: *const wasmer_export_t, + memory: *mut *mut wasmer_memory_t, +) -> wasmer_result_t { + let named_export = &*(export as *const NamedExport); + let export = &named_export.export; + + if let Export::Memory(exported_memory) = export { + *memory = exported_memory as *const Memory as *mut wasmer_memory_t; + wasmer_result_t::WASMER_OK + } else { + update_last_error(CApiError { + msg: "cannot cast the `wasmer_export_t` pointer to a `wasmer_memory_t` \ + pointer because it does not represent a memory export." + .to_string(), + }); + wasmer_result_t::WASMER_ERROR + } +} + +/// 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 + } + } +} + +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, + } + } +} + +pub(crate) struct NamedExport { + pub(crate) name: String, + pub(crate) export: Export, + pub(crate) instance: *mut Instance, +} + +pub(crate) struct NamedExportDescriptor { + name: String, + kind: wasmer_import_export_kind, +} diff --git a/lib/runtime-c-api/src/global.rs b/lib/runtime-c-api/src/global.rs new file mode 100644 index 000000000..2e4ea645f --- /dev/null +++ b/lib/runtime-c-api/src/global.rs @@ -0,0 +1,70 @@ +//! Wasm global. + +use crate::value::{wasmer_value_t, wasmer_value_tag}; +use wasmer_runtime::Global; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_global_descriptor_t { + mutable: bool, + kind: wasmer_value_tag, +} + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_global_t; + +/// 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() { + unsafe { Box::from_raw(global as *mut Global) }; + } +} diff --git a/lib/runtime-c-api/src/import.rs b/lib/runtime-c-api/src/import.rs new file mode 100644 index 000000000..cc3598078 --- /dev/null +++ b/lib/runtime-c-api/src/import.rs @@ -0,0 +1,358 @@ +//! Wasm imports. + +use crate::{ + error::{update_last_error, CApiError}, + export::{wasmer_import_export_kind, wasmer_import_export_value}, + module::wasmer_module_t, + value::wasmer_value_tag, + wasmer_byte_array, wasmer_result_t, +}; +use libc::{c_int, uint32_t}; +use std::{ffi::c_void, ptr, slice, sync::Arc}; +use wasmer_runtime::Module; +use wasmer_runtime_core::{ + export::{Context, Export, FuncPointer}, + module::ImportName, + types::{FuncSig, Type}, +}; + +#[repr(C)] +pub struct wasmer_import_t { + pub module_name: wasmer_byte_array, + pub import_name: wasmer_byte_array, + pub tag: wasmer_import_export_kind, + pub value: wasmer_import_export_value, +} + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_import_func_t; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_import_descriptor_t; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_import_descriptors_t; + +/// 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 extern "C" fn wasmer_import_descriptors_destroy( + import_descriptors: *mut wasmer_import_descriptors_t, +) { + if !import_descriptors.is_null() { + unsafe { 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() +} + +/// 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() { + unsafe { Box::from_raw(func as *mut Export) }; + } +} + +struct NamedImportDescriptor { + module: String, + name: String, + kind: wasmer_import_export_kind, +} diff --git a/lib/runtime-c-api/src/instance.rs b/lib/runtime-c-api/src/instance.rs new file mode 100644 index 000000000..a9d7d5555 --- /dev/null +++ b/lib/runtime-c-api/src/instance.rs @@ -0,0 +1,256 @@ +//! Wasm instance. + +use crate::{ + error::{update_last_error, CApiError}, + export::{wasmer_exports_t, wasmer_import_export_kind, NamedExport, NamedExports}, + import::wasmer_import_t, + memory::wasmer_memory_t, + value::{wasmer_value, wasmer_value_t, wasmer_value_tag}, + wasmer_result_t, +}; +use libc::{c_char, c_int, c_void, uint32_t, uint8_t}; +use std::{collections::HashMap, ffi::CStr, slice}; +use wasmer_runtime::{Ctx, Global, ImportObject, Instance, Memory, Table, Value}; +use wasmer_runtime_core::{export::Export, import::Namespace}; + +#[repr(C)] +pub struct wasmer_instance_t; + +#[repr(C)] +pub struct wasmer_instance_context_t; + +/// 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] = 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; +} + +/// 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 +} + +/// 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() { + unsafe { Box::from_raw(instance as *mut Instance) }; + } +} diff --git a/lib/runtime-c-api/src/lib.rs b/lib/runtime-c-api/src/lib.rs index b63e69d9c..f4d4cc2be 100644 --- a/lib/runtime-c-api/src/lib.rs +++ b/lib/runtime-c-api/src/lib.rs @@ -1,30 +1,19 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + 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}; +use libc::{uint32_t, uint8_t}; -#[repr(C)] -pub struct wasmer_module_t; - -#[repr(C)] -pub struct wasmer_instance_t; - -#[repr(C)] -pub struct wasmer_instance_context_t; +pub mod error; +pub mod export; +pub mod global; +pub mod import; +pub mod instance; +pub mod memory; +pub mod module; +pub mod table; +pub mod value; #[allow(non_camel_case_types)] #[repr(C)] @@ -33,59 +22,6 @@ pub enum wasmer_result_t { 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, @@ -98,1515 +34,8 @@ pub struct wasmer_limit_option_t { 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/src/memory.rs b/lib/runtime-c-api/src/memory.rs new file mode 100644 index 000000000..f90965586 --- /dev/null +++ b/lib/runtime-c-api/src/memory.rs @@ -0,0 +1,108 @@ +//! Wasm memory.o + +use crate::{error::update_last_error, wasmer_limits_t, wasmer_result_t}; +use libc::{uint32_t, uint8_t}; +use std::cell::Cell; +use wasmer_runtime::Memory; +use wasmer_runtime_core::{ + types::MemoryDescriptor, + units::{Bytes, Pages}, +}; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_memory_t; + +/// 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 +} + +/// 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 = unsafe { &*(mem as *const Memory) }; + 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 Memory +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_memory_destroy(memory: *mut wasmer_memory_t) { + if !memory.is_null() { + unsafe { Box::from_raw(memory as *mut Memory) }; + } +} diff --git a/lib/runtime-c-api/src/module.rs b/lib/runtime-c-api/src/module.rs new file mode 100644 index 000000000..42d9815a6 --- /dev/null +++ b/lib/runtime-c-api/src/module.rs @@ -0,0 +1,289 @@ +//! Wasm module. + +use crate::{ + error::{update_last_error, CApiError}, + export::wasmer_import_export_kind, + import::wasmer_import_t, + instance::wasmer_instance_t, + wasmer_byte_array, wasmer_result_t, +}; +use libc::{c_int, uint32_t, uint8_t}; +use std::{collections::HashMap, slice}; +use wasmer_runtime::{compile, default_compiler, Global, ImportObject, Memory, Module, Table}; +use wasmer_runtime_core::{cache::Artifact, export::Export, import::Namespace, load_cache_with}; + +#[repr(C)] +pub struct wasmer_module_t; + +#[repr(C)] +pub struct wasmer_serialized_module_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. +#[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] = slice::from_raw_parts_mut(wasm_bytes, wasm_bytes_len as usize); + let result = 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 +} + +/// 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: *const uint8_t, + wasm_bytes_len: uint32_t, +) -> bool { + if wasm_bytes.is_null() { + return false; + } + let bytes: &[u8] = slice::from_raw_parts(wasm_bytes, wasm_bytes_len as usize); + + wasmer_runtime_core::validate(bytes) +} + +/// 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 +} + +/// Serialize the given Module. +/// +/// The caller owns the object and should call `wasmer_serialized_module_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. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_module_serialize( + serialized_module: *mut *mut wasmer_serialized_module_t, + module: *const wasmer_module_t, +) -> wasmer_result_t { + let module = &*(module as *const Module); + + match module.cache() { + Ok(artifact) => match artifact.serialize() { + Ok(serialized_artifact) => { + *serialized_module = Box::into_raw(Box::new(serialized_artifact)) as _; + + wasmer_result_t::WASMER_OK + } + Err(_) => { + update_last_error(CApiError { + msg: "Failed to serialize the module artifact".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } + }, + Err(_) => { + update_last_error(CApiError { + msg: "Failed to serialize the module".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } + } +} + +/// Get bytes of the serialized module. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_serialized_module_bytes( + serialized_module: *const wasmer_serialized_module_t, +) -> wasmer_byte_array { + let serialized_module = &*(serialized_module as *const &[u8]); + + wasmer_byte_array { + bytes: serialized_module.as_ptr(), + bytes_len: serialized_module.len() as u32, + } +} + +/// Transform a sequence of bytes into a serialized module. +/// +/// The caller owns the object and should call `wasmer_serialized_module_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. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_serialized_module_from_bytes( + serialized_module: *mut *mut wasmer_serialized_module_t, + serialized_module_bytes: *const uint8_t, + serialized_module_bytes_length: uint32_t, +) -> wasmer_result_t { + if serialized_module.is_null() { + update_last_error(CApiError { + msg: "`serialized_module_bytes` pointer is null".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + } + + let serialized_module_bytes: &[u8] = slice::from_raw_parts( + serialized_module_bytes, + serialized_module_bytes_length as usize, + ); + + *serialized_module = Box::into_raw(Box::new(serialized_module_bytes)) as _; + wasmer_result_t::WASMER_OK +} + +/// Deserialize the given serialized module. +/// +/// 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_deserialize( + module: *mut *mut wasmer_module_t, + serialized_module: *const wasmer_serialized_module_t, +) -> wasmer_result_t { + if serialized_module.is_null() { + update_last_error(CApiError { + msg: "`serialized_module` pointer is null".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + } + + let serialized_module: &[u8] = &*(serialized_module as *const &[u8]); + + match Artifact::deserialize(serialized_module) { + Ok(artifact) => match load_cache_with(artifact, default_compiler()) { + Ok(deserialized_module) => { + *module = Box::into_raw(Box::new(deserialized_module)) as _; + wasmer_result_t::WASMER_OK + } + Err(_) => { + update_last_error(CApiError { + msg: "Failed to compile the serialized module".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } + }, + Err(_) => { + update_last_error(CApiError { + msg: "Failed to deserialize the module".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } + } +} + +/// Frees memory for the given serialized Module. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_serialized_module_destroy( + serialized_module: *mut wasmer_serialized_module_t, +) { + if !serialized_module.is_null() { + unsafe { Box::from_raw(serialized_module as *mut &[u8]) }; + } +} + +/// 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() { + unsafe { Box::from_raw(module as *mut Module) }; + } +} diff --git a/lib/runtime-c-api/src/table.rs b/lib/runtime-c-api/src/table.rs new file mode 100644 index 000000000..84d3b794f --- /dev/null +++ b/lib/runtime-c-api/src/table.rs @@ -0,0 +1,86 @@ +//! Wasm tables. + +use crate::{error::update_last_error, wasmer_limits_t, wasmer_result_t}; +use libc::uint32_t; +use wasmer_runtime::Table; +use wasmer_runtime_core::types::{ElementType, TableDescriptor}; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_table_t; + +/// 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() { + unsafe { Box::from_raw(table as *mut Table) }; + } +} diff --git a/lib/runtime-c-api/src/value.rs b/lib/runtime-c-api/src/value.rs new file mode 100644 index 000000000..c0a3e64d8 --- /dev/null +++ b/lib/runtime-c-api/src/value.rs @@ -0,0 +1,119 @@ +//! Wasm values. + +use libc::{int32_t, int64_t}; +use wasmer_runtime::Value; +use wasmer_runtime_core::types::Type; + +#[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)] +#[allow(non_snake_case)] +pub union wasmer_value { + pub I32: int32_t, + pub I64: int64_t, + pub F32: f32, + pub F64: f64, +} + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_value_t { + pub tag: wasmer_value_tag, + pub value: wasmer_value, +} + +impl From for Value { + fn from(v: wasmer_value_t) -> Self { + unsafe { + #[allow(unreachable_patterns, non_snake_case)] + 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 { + #[allow(unreachable_patterns)] + 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 { + #[allow(unreachable_patterns)] + 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, + } + } +} diff --git a/lib/runtime-c-api/tests/.gitignore b/lib/runtime-c-api/tests/.gitignore index 15e33d2cf..ed3b6ed6e 100644 --- a/lib/runtime-c-api/tests/.gitignore +++ b/lib/runtime-c-api/tests/.gitignore @@ -9,15 +9,17 @@ install_manifest.txt compile_commands.json CTestTestfile.cmake _deps -test-globals +rust-build +test-exported-memory test-exports -test-instantiate -test-imports +test-globals test-import-function +test-imports +test-instantiate test-memory -test-module-imports test-module test-module-exports +test-module-imports +test-module-serialize test-tables -test-validate -rust-build \ No newline at end of file +test-validate \ No newline at end of file diff --git a/lib/runtime-c-api/tests/CMakeLists.txt b/lib/runtime-c-api/tests/CMakeLists.txt index c94c748e1..ef9804f01 100644 --- a/lib/runtime-c-api/tests/CMakeLists.txt +++ b/lib/runtime-c-api/tests/CMakeLists.txt @@ -1,17 +1,19 @@ cmake_minimum_required (VERSION 2.6) project (WasmerRuntimeCApiTests) -add_executable(test-imports test-imports.c) +add_executable(test-exported-memory test-exported-memory.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-imports test-imports.c) +add_executable(test-instantiate test-instantiate.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-module-imports test-module-imports.c) +add_executable(test-module-serialize test-module-serialize.c) add_executable(test-tables test-tables.c) +add_executable(test-validate test-validate.c) find_library( WASMER_LIB NAMES libwasmer_runtime_c_api.dylib libwasmer_runtime_c_api.so libwasmer_runtime_c_api.dll @@ -34,9 +36,9 @@ set( "/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-exported-memory general ${WASMER_LIB}) +target_compile_options(test-exported-memory PRIVATE ${COMPILER_OPTIONS}) +add_test(test-exported-memory test-exported-memory) target_link_libraries(test-exports general ${WASMER_LIB}) target_compile_options(test-exports PRIVATE ${COMPILER_OPTIONS}) @@ -46,22 +48,22 @@ 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-imports general ${WASMER_LIB}) +target_compile_options(test-imports PRIVATE ${COMPILER_OPTIONS}) +add_test(test-imports test-imports) + +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-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) @@ -70,10 +72,18 @@ 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-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-serialize general ${WASMER_LIB}) +target_compile_options(test-module-serialize PRIVATE ${COMPILER_OPTIONS}) +add_test(test-module-serialize test-module-serialize) target_link_libraries(test-tables general ${WASMER_LIB}) target_compile_options(test-tables PRIVATE ${COMPILER_OPTIONS}) add_test(test-tables test-tables) + +target_link_libraries(test-validate general ${WASMER_LIB}) +target_compile_options(test-validate PRIVATE ${COMPILER_OPTIONS}) +add_test(test-validate test-validate) diff --git a/lib/runtime-c-api/tests/hello_wasm.wasm b/lib/runtime-c-api/tests/assets/hello_wasm.wasm similarity index 100% rename from lib/runtime-c-api/tests/hello_wasm.wasm rename to lib/runtime-c-api/tests/assets/hello_wasm.wasm diff --git a/lib/runtime-c-api/tests/assets/return_hello.rs b/lib/runtime-c-api/tests/assets/return_hello.rs new file mode 100644 index 000000000..39e770193 --- /dev/null +++ b/lib/runtime-c-api/tests/assets/return_hello.rs @@ -0,0 +1,4 @@ +#[no_mangle] +pub extern "C" fn return_hello() -> *const u8 { + b"Hello, World!\0"[..].as_ptr() +} diff --git a/lib/runtime-c-api/tests/assets/return_hello.wasm b/lib/runtime-c-api/tests/assets/return_hello.wasm new file mode 100644 index 000000000..a7f79c376 Binary files /dev/null and b/lib/runtime-c-api/tests/assets/return_hello.wasm differ diff --git a/lib/runtime-c-api/tests/sum.wasm b/lib/runtime-c-api/tests/assets/sum.wasm similarity index 100% rename from lib/runtime-c-api/tests/sum.wasm rename to lib/runtime-c-api/tests/assets/sum.wasm diff --git a/lib/runtime-c-api/tests/wasm_sample_app.wasm b/lib/runtime-c-api/tests/assets/wasm_sample_app.wasm similarity index 100% rename from lib/runtime-c-api/tests/wasm_sample_app.wasm rename to lib/runtime-c-api/tests/assets/wasm_sample_app.wasm diff --git a/lib/runtime-c-api/tests/test-exported-memory.c b/lib/runtime-c-api/tests/test-exported-memory.c new file mode 100644 index 000000000..455de0402 --- /dev/null +++ b/lib/runtime-c-api/tests/test-exported-memory.c @@ -0,0 +1,73 @@ +#include +#include "../wasmer.h" +#include +#include +#include + +int main() +{ + // Read the wasm file bytes + FILE *file = fopen("assets/return_hello.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); + + // Instantiate the module. + 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); + + // Call the `return_hello` function. + wasmer_value_t params[] = {}; + wasmer_value_t result; + wasmer_value_t results[] = {result}; + + wasmer_result_t call_result = wasmer_instance_call(instance, "return_hello", params, 0, results, 1); + printf("Call result: %d\n", call_result); + printf("Result: %d\n", results[0].value.I32); + assert(call_result == WASMER_OK); + assert(results[0].value.I32 == 1048576); + + // Get all exports. + wasmer_exports_t *exports = NULL; + wasmer_instance_exports(instance, &exports); + + int export_length = wasmer_exports_len(exports); + printf("exports_length: %d\n", export_length); + assert(export_length == 5); + + // Get the `memory` export. + wasmer_export_t *export = wasmer_exports_get(exports, 1); + wasmer_import_export_kind kind = wasmer_export_kind(export); + assert(kind == WASM_MEMORY); + + wasmer_byte_array export_name = wasmer_export_name(export); + printf("export_name: `%.*s`\n", export_name.bytes_len, export_name.bytes); + + // Cast the export into a memory. + wasmer_memory_t *memory; + wasmer_result_t export_to_memory_result = wasmer_export_to_memory(export, &memory); + printf("Export to memory result: %d\n", export_to_memory_result); + printf("Memory pointer: %p\n", memory); + assert(export_to_memory_result == WASMER_OK); + + uint32_t memory_length = wasmer_memory_length(memory); + assert(memory_length == 17); + + // Read the data from the memory. + uint8_t *memory_data = wasmer_memory_data(memory); + uint8_t *returned_string = memory_data + results[0].value.I32; + + printf("Returned string from Wasm: %s\n", returned_string); + assert(strcmp("Hello, World!", (const char *) returned_string) == 0); + + printf("Destroy instance\n"); + wasmer_instance_destroy(instance); + + return 0; +} diff --git a/lib/runtime-c-api/tests/test-exports.c b/lib/runtime-c-api/tests/test-exports.c index b282f1911..951c4f3a4 100644 --- a/lib/runtime-c-api/tests/test-exports.c +++ b/lib/runtime-c-api/tests/test-exports.c @@ -6,7 +6,7 @@ int main() { // Read the wasm file bytes - FILE *file = fopen("sum.wasm", "r"); + FILE *file = fopen("assets/sum.wasm", "r"); fseek(file, 0, SEEK_END); long len = ftell(file); uint8_t *bytes = malloc(len); @@ -17,14 +17,14 @@ int main() 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); + 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); + printf("exports_len: %d\n", exports_len); assert(exports_len == 1); wasmer_export_t *export = wasmer_exports_get(exports, 0); diff --git a/lib/runtime-c-api/tests/test-import-function.c b/lib/runtime-c-api/tests/test-import-function.c index ce6736deb..92e3e821d 100644 --- a/lib/runtime-c-api/tests/test-import-function.c +++ b/lib/runtime-c-api/tests/test-import-function.c @@ -57,7 +57,7 @@ int main() wasmer_import_t imports[] = {import}; // Read the wasm file bytes - FILE *file = fopen("wasm_sample_app.wasm", "r"); + FILE *file = fopen("assets/wasm_sample_app.wasm", "r"); fseek(file, 0, SEEK_END); long len = ftell(file); uint8_t *bytes = malloc(len); diff --git a/lib/runtime-c-api/tests/test-imports.c b/lib/runtime-c-api/tests/test-imports.c index 9c0c45467..4713719ed 100644 --- a/lib/runtime-c-api/tests/test-imports.c +++ b/lib/runtime-c-api/tests/test-imports.c @@ -117,7 +117,7 @@ int main() wasmer_import_t imports[] = {func_import, global_import, memory_import, table_import}; // Read the wasm file bytes - FILE *file = fopen("hello_wasm.wasm", "r"); + FILE *file = fopen("assets/hello_wasm.wasm", "r"); fseek(file, 0, SEEK_END); long len = ftell(file); uint8_t *bytes = malloc(len); diff --git a/lib/runtime-c-api/tests/test-instantiate.c b/lib/runtime-c-api/tests/test-instantiate.c index e7fae37e4..8a7c2610e 100644 --- a/lib/runtime-c-api/tests/test-instantiate.c +++ b/lib/runtime-c-api/tests/test-instantiate.c @@ -7,7 +7,7 @@ int main() { // Read the wasm file bytes - FILE *file = fopen("sum.wasm", "r"); + FILE *file = fopen("assets/sum.wasm", "r"); fseek(file, 0, SEEK_END); long len = ftell(file); uint8_t *bytes = malloc(len); @@ -46,7 +46,8 @@ int main() 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); + int error_result = wasmer_last_error_message(error_str, error_len); + assert(error_len == error_result); 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); diff --git a/lib/runtime-c-api/tests/test-module-exports.c b/lib/runtime-c-api/tests/test-module-exports.c index 48fe6f25b..f290ef45b 100644 --- a/lib/runtime-c-api/tests/test-module-exports.c +++ b/lib/runtime-c-api/tests/test-module-exports.c @@ -6,7 +6,7 @@ int main() { // Read the wasm file bytes - FILE *file = fopen("sum.wasm", "r"); + FILE *file = fopen("assets/sum.wasm", "r"); fseek(file, 0, SEEK_END); long len = ftell(file); uint8_t *bytes = malloc(len); @@ -50,4 +50,4 @@ int main() 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 index b63d308f2..532005928 100644 --- a/lib/runtime-c-api/tests/test-module-imports.c +++ b/lib/runtime-c-api/tests/test-module-imports.c @@ -6,7 +6,7 @@ int main() { // Read the wasm file bytes - FILE *file = fopen("wasm_sample_app.wasm", "r"); + FILE *file = fopen("assets/wasm_sample_app.wasm", "r"); fseek(file, 0, SEEK_END); long len = ftell(file); uint8_t *bytes = malloc(len); diff --git a/lib/runtime-c-api/tests/test-module-serialize.c b/lib/runtime-c-api/tests/test-module-serialize.c new file mode 100644 index 000000000..b98418646 --- /dev/null +++ b/lib/runtime-c-api/tests/test-module-serialize.c @@ -0,0 +1,93 @@ +#include +#include "../wasmer.h" +#include +#include + +int main() +{ + // Read the wasm file bytes + FILE *file = fopen("assets/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_one = NULL; + wasmer_result_t compile_result = wasmer_compile(&module_one, bytes, len); + printf("Compile result: %d\n", compile_result); + assert(compile_result == WASMER_OK); + + wasmer_serialized_module_t *serialized_module = NULL; + wasmer_result_t serialize_result = wasmer_module_serialize(&serialized_module, module_one); + printf("Serialize result: %d\n", serialize_result); + assert(serialize_result == WASMER_OK); + + wasmer_byte_array serialized_module_bytes = wasmer_serialized_module_bytes(serialized_module); + printf("Serialized module pointer: %p\n", serialized_module_bytes.bytes); + printf("Serialized module length: %d\n", serialized_module_bytes.bytes_len); + assert(serialized_module_bytes.bytes != NULL); + assert(serialized_module_bytes.bytes_len > 8); + assert(serialized_module_bytes.bytes[0] == 'W'); + assert(serialized_module_bytes.bytes[1] == 'A'); + assert(serialized_module_bytes.bytes[2] == 'S'); + assert(serialized_module_bytes.bytes[3] == 'M'); + assert(serialized_module_bytes.bytes[4] == 'E'); + assert(serialized_module_bytes.bytes[5] == 'R'); + + wasmer_module_t *module_two = NULL; + wasmer_result_t unserialize_result = wasmer_module_deserialize(&module_two, serialized_module); + assert(unserialize_result == WASMER_OK); + + wasmer_import_t imports[] = {}; + wasmer_instance_t *instance = NULL; + wasmer_result_t instantiate_result = wasmer_module_instantiate(module_two, &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); + + wasmer_serialized_module_t *serialized_module_two = NULL; + wasmer_result_t serialized_module_from_bytes_result = wasmer_serialized_module_from_bytes( + &serialized_module_two, + serialized_module_bytes.bytes, + serialized_module_bytes.bytes_len + ); + assert(serialized_module_from_bytes_result == WASMER_OK); + + wasmer_module_t *module_three = NULL; + wasmer_result_t unserialized_result_two = wasmer_module_deserialize(&module_three, serialized_module_two); + assert(unserialized_result_two == WASMER_OK); + + wasmer_instance_t *instance_two = NULL; + wasmer_result_t instantiate_result_two = wasmer_module_instantiate(module_three, &instance, imports, 0); + assert(instantiate_result_two == WASMER_OK); + + printf("Destroy the serialized module\n"); + wasmer_serialized_module_destroy(serialized_module); + wasmer_serialized_module_destroy(serialized_module_two); + + printf("Destroy instance\n"); + wasmer_instance_destroy(instance); + + printf("Destroy modules\n"); + wasmer_module_destroy(module_one); + wasmer_module_destroy(module_two); + return 0; +} diff --git a/lib/runtime-c-api/tests/test-module.c b/lib/runtime-c-api/tests/test-module.c index 062caf5b8..a3f21f6a1 100644 --- a/lib/runtime-c-api/tests/test-module.c +++ b/lib/runtime-c-api/tests/test-module.c @@ -6,7 +6,7 @@ int main() { // Read the wasm file bytes - FILE *file = fopen("sum.wasm", "r"); + FILE *file = fopen("assets/sum.wasm", "r"); fseek(file, 0, SEEK_END); long len = ftell(file); uint8_t *bytes = malloc(len); @@ -48,4 +48,4 @@ int main() 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-validate.c b/lib/runtime-c-api/tests/test-validate.c index 689cf50f2..8ce135d82 100644 --- a/lib/runtime-c-api/tests/test-validate.c +++ b/lib/runtime-c-api/tests/test-validate.c @@ -6,7 +6,7 @@ int main() { // Read the wasm file bytes - FILE *file = fopen("sum.wasm", "r"); + FILE *file = fopen("assets/sum.wasm", "r"); fseek(file, 0, SEEK_END); long len = ftell(file); uint8_t *bytes = malloc(len); diff --git a/lib/runtime-c-api/wasmer.h b/lib/runtime-c-api/wasmer.h index 43d97ecb8..d11deab11 100644 --- a/lib/runtime-c-api/wasmer.h +++ b/lib/runtime-c-api/wasmer.h @@ -66,6 +66,10 @@ typedef struct { typedef struct { +} wasmer_memory_t; + +typedef struct { + } wasmer_exports_t; typedef struct { @@ -99,10 +103,6 @@ typedef struct { typedef struct { -} wasmer_memory_t; - -typedef struct { - } wasmer_table_t; typedef union { @@ -129,6 +129,10 @@ typedef struct { wasmer_limit_option_t max; } wasmer_limits_t; +typedef struct { + +} wasmer_serialized_module_t; + /** * Creates a new Module from the given wasm bytes. * Returns `wasmer_result_t::WASMER_OK` upon success. @@ -237,6 +241,14 @@ wasmer_byte_array wasmer_export_name(wasmer_export_t *export_); */ const wasmer_export_func_t *wasmer_export_to_func(const wasmer_export_t *export_); +/** + * Gets a memory pointer from an export 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_to_memory(const wasmer_export_t *export_, wasmer_memory_t **memory); + /** * Frees the memory for the given exports */ @@ -488,6 +500,15 @@ uint32_t wasmer_memory_length(const wasmer_memory_t *memory); */ wasmer_result_t wasmer_memory_new(wasmer_memory_t **memory, wasmer_limits_t limits); +/** + * Deserialize the given serialized module. + * 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_deserialize(wasmer_module_t **module, + const wasmer_serialized_module_t *serialized_module); + /** * Frees memory for the given Module */ @@ -504,6 +525,37 @@ wasmer_result_t wasmer_module_instantiate(const wasmer_module_t *module, wasmer_import_t *imports, int imports_len); +/** + * Serialize the given Module. + * The caller owns the object and should call `wasmer_serialized_module_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_module_serialize(wasmer_serialized_module_t **serialized_module, + const wasmer_module_t *module); + +/** + * Get bytes of the serialized module. + */ +wasmer_byte_array wasmer_serialized_module_bytes(const wasmer_serialized_module_t *serialized_module); + +/** + * Frees memory for the given serialized Module. + */ +void wasmer_serialized_module_destroy(wasmer_serialized_module_t *serialized_module); + +/** + * Transform a sequence of bytes into a serialized module. + * The caller owns the object and should call `wasmer_serialized_module_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_serialized_module_from_bytes(wasmer_serialized_module_t **serialized_module, + const uint8_t *serialized_module_bytes, + uint32_t serialized_module_bytes_length); + /** * Frees memory for the given Table */ @@ -535,6 +587,6 @@ 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); +bool wasmer_validate(const 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 index 76c27d46e..7b22732d0 100644 --- a/lib/runtime-c-api/wasmer.hh +++ b/lib/runtime-c-api/wasmer.hh @@ -61,6 +61,10 @@ struct wasmer_export_t { }; +struct wasmer_memory_t { + +}; + struct wasmer_exports_t { }; @@ -94,10 +98,6 @@ struct wasmer_instance_context_t { }; -struct wasmer_memory_t { - -}; - struct wasmer_table_t { }; @@ -126,6 +126,10 @@ struct wasmer_limits_t { wasmer_limit_option_t max; }; +struct wasmer_serialized_module_t { + +}; + extern "C" { /// Creates a new Module from the given wasm bytes. @@ -206,6 +210,12 @@ 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_); +/// Gets a memory pointer from an export 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_to_memory(const wasmer_export_t *export_, wasmer_memory_t **memory); + /// Frees the memory for the given exports void wasmer_exports_destroy(wasmer_exports_t *exports); @@ -385,6 +395,13 @@ uint32_t wasmer_memory_length(const wasmer_memory_t *memory); /// and `wasmer_last_error_message` to get an error message. wasmer_result_t wasmer_memory_new(wasmer_memory_t **memory, wasmer_limits_t limits); +/// Deserialize the given serialized module. +/// 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_deserialize(wasmer_module_t **module, + const wasmer_serialized_module_t *serialized_module); + /// Frees memory for the given Module void wasmer_module_destroy(wasmer_module_t *module); @@ -397,6 +414,29 @@ wasmer_result_t wasmer_module_instantiate(const wasmer_module_t *module, wasmer_import_t *imports, int imports_len); +/// Serialize the given Module. +/// The caller owns the object and should call `wasmer_serialized_module_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_module_serialize(wasmer_serialized_module_t **serialized_module, + const wasmer_module_t *module); + +/// Get bytes of the serialized module. +wasmer_byte_array wasmer_serialized_module_bytes(const wasmer_serialized_module_t *serialized_module); + +/// Frees memory for the given serialized Module. +void wasmer_serialized_module_destroy(wasmer_serialized_module_t *serialized_module); + +/// Transform a sequence of bytes into a serialized module. +/// The caller owns the object and should call `wasmer_serialized_module_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_serialized_module_from_bytes(wasmer_serialized_module_t **serialized_module, + const uint8_t *serialized_module_bytes, + uint32_t serialized_module_bytes_length); + /// Frees memory for the given Table void wasmer_table_destroy(wasmer_table_t *table); @@ -418,7 +458,7 @@ uint32_t wasmer_table_length(wasmer_table_t *table); 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); +bool wasmer_validate(const uint8_t *wasm_bytes, uint32_t wasm_bytes_len); } // extern "C" diff --git a/lib/runtime-core/Cargo.toml b/lib/runtime-core/Cargo.toml index c6ac22f40..d6b735cff 100644 --- a/lib/runtime-core/Cargo.toml +++ b/lib/runtime-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime-core" -version = "0.2.1" +version = "0.4.1" description = "Wasmer runtime core library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -10,13 +10,14 @@ edition = "2018" [dependencies] nix = "0.12.0" page_size = "0.4.1" -wasmparser = "0.23.0" +wasmparser = "0.29.2" parking_lot = "0.7.1" lazy_static = "1.2.0" indexmap = "1.0.2" errno = "0.2.4" libc = "0.2.49" hex = "0.3.2" +smallvec = "0.6.9" # Dependencies for caching. [dependencies.serde] @@ -43,6 +44,9 @@ winapi = { version = "0.3", features = ["memoryapi"] } [dev-dependencies] field-offset = "0.1.1" +[build-dependencies] +blake2b_simd = "0.4.1" +rustc_version = "0.2.3" + [features] debug = [] - diff --git a/lib/runtime-core/README.md b/lib/runtime-core/README.md new file mode 100644 index 000000000..0bbb74379 --- /dev/null +++ b/lib/runtime-core/README.md @@ -0,0 +1,31 @@ +

+ + Wasmer logo + +

+ +

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

+ +# Wasmer Runtime Core + +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 represents the core of the runtime. diff --git a/lib/runtime-core/build.rs b/lib/runtime-core/build.rs new file mode 100644 index 000000000..38071e31c --- /dev/null +++ b/lib/runtime-core/build.rs @@ -0,0 +1,31 @@ +use blake2b_simd::blake2bp; +use std::{env, fs, io::Write, path::PathBuf}; + +const WASMER_VERSION: &'static str = env!("CARGO_PKG_VERSION"); + +fn main() { + let mut state = blake2bp::State::new(); + state.update(WASMER_VERSION.as_bytes()); + + let hasher = state.finalize(); + let hash_string = hasher.to_hex().as_str().to_owned(); + + let crate_dir = env::var("OUT_DIR").unwrap(); + let wasmer_version_hash_file = { + let mut path = PathBuf::from(&crate_dir); + path.push("wasmer_version_hash.txt"); + path + }; + + let mut f_out = fs::File::create(wasmer_version_hash_file) + .expect("Could not create file for wasmer hash value"); + + f_out + .write_all(hash_string.as_bytes()) + .expect("Could not write to file for wasmer hash value"); + + // 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/runtime-core/src/backend.rs b/lib/runtime-core/src/backend.rs index 47f0fbae9..d465cda58 100644 --- a/lib/runtime-core/src/backend.rs +++ b/lib/runtime-core/src/backend.rs @@ -1,9 +1,8 @@ use crate::{ - backing::ImportBacking, error::CompileResult, - error::RuntimeResult, module::ModuleInner, - types::{FuncIndex, LocalFuncIndex, Value}, + typed_func::Wasm, + types::{LocalFuncIndex, SigIndex}, vm, }; @@ -14,6 +13,8 @@ use crate::{ }; use std::{any::Any, ptr::NonNull}; +use hashbrown::HashMap; + pub mod sys { pub use crate::sys::*; } @@ -22,6 +23,7 @@ pub use crate::sig_registry::SigRegistry; #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)] pub enum Backend { Cranelift, + Singlepass, LLVM, } @@ -37,64 +39,49 @@ impl Token { } } +/// Configuration data for the compiler +pub struct CompilerConfig { + /// Symbol information generated from emscripten; used for more detailed debug messages + pub symbol_map: Option>, +} + +impl Default for CompilerConfig { + fn default() -> CompilerConfig { + CompilerConfig { symbol_map: None } + } +} + pub trait Compiler { /// Compiles a `Module` from WebAssembly binary format. /// The `CompileToken` parameter ensures that this can only /// be called from inside the runtime. - fn compile(&self, wasm: &[u8], _: Token) -> CompileResult; + fn compile( + &self, + wasm: &[u8], + comp_conf: CompilerConfig, + _: Token, + ) -> CompileResult; unsafe fn from_cache(&self, cache: Artifact, _: Token) -> Result; } -/// The functionality exposed by this trait is expected to be used -/// for calling functions exported by a webassembly module from -/// host code only. -pub trait ProtectedCaller: Send + Sync { - /// This calls the exported function designated by `local_func_index`. - /// Important to note, this supports calling imported functions that are - /// then exported. - /// - /// It's invalid to attempt to call a local function that isn't exported and - /// the implementation is expected to check for that. The implementation - /// is also expected to check for correct parameter types and correct - /// parameter number. - /// - /// The `returns` parameter is filled with dummy values when passed in and upon function - /// return, will be filled with the return values of the wasm function, as long as the - /// call completed successfully. - /// - /// The existance of the Token parameter ensures that this can only be called from - /// within the runtime crate. - fn call( +pub trait RunnableModule: Send + Sync { + /// This returns a pointer to the function designated by the `local_func_index` + /// parameter. + fn get_func( &self, - module: &ModuleInner, - func_index: FuncIndex, - params: &[Value], - import_backing: &ImportBacking, - vmctx: *mut vm::Ctx, - _: Token, - ) -> RuntimeResult>; + info: &ModuleInfo, + local_func_index: LocalFuncIndex, + ) -> Option>; - fn get_early_trapper(&self) -> Box; -} + /// A wasm trampoline contains the necesarry data to dynamically call an exported wasm function. + /// Given a particular signature index, we are returned a trampoline that is matched with that + /// signature and an invoke function that can call the trampoline. + fn get_trampoline(&self, info: &ModuleInfo, sig_index: SigIndex) -> Option; -pub trait UserTrapper { unsafe fn do_early_trap(&self, data: Box) -> !; } -pub trait FuncResolver: Send + Sync { - /// This returns a pointer to the function designated by the `local_func_index` - /// parameter. - fn get( - &self, - module: &ModuleInner, - local_func_index: LocalFuncIndex, - ) -> Option>; -} - pub trait CacheGen: Send + Sync { - fn generate_cache( - &self, - module: &ModuleInner, - ) -> Result<(Box, Box<[u8]>, Memory), CacheError>; + fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError>; } diff --git a/lib/runtime-core/src/backing.rs b/lib/runtime-core/src/backing.rs index 6afb7ddb2..14fe41cbf 100644 --- a/lib/runtime-core/src/backing.rs +++ b/lib/runtime-core/src/backing.rs @@ -73,8 +73,8 @@ impl LocalBacking { (0..module.info.func_assoc.len() - module.info.imported_functions.len()) .map(|index| { module - .func_resolver - .get(module, LocalFuncIndex::new(index)) + .runnable_module + .get_func(&module.info, LocalFuncIndex::new(index)) .unwrap() .as_ptr() as *const _ }) @@ -223,8 +223,8 @@ impl LocalBacking { let (func, ctx) = match func_index.local_or_import(&module.info) { LocalOrImport::Local(local_func_index) => ( module - .func_resolver - .get(module, local_func_index) + .runnable_module + .get_func(&module.info, local_func_index) .unwrap() .as_ptr() as *const vm::Func, @@ -262,8 +262,8 @@ impl LocalBacking { let (func, ctx) = match func_index.local_or_import(&module.info) { LocalOrImport::Local(local_func_index) => ( module - .func_resolver - .get(module, local_func_index) + .runnable_module + .get_func(&module.info, local_func_index) .unwrap() .as_ptr() as *const vm::Func, diff --git a/lib/runtime-core/src/cache.rs b/lib/runtime-core/src/cache.rs index 1cb804655..93e14b0f9 100644 --- a/lib/runtime-core/src/cache.rs +++ b/lib/runtime-core/src/cache.rs @@ -207,3 +207,7 @@ pub trait Cache { fn load(&self, key: WasmHash) -> Result; fn store(&mut self, key: WasmHash, module: Module) -> Result<(), Self::StoreError>; } + +/// A unique ID generated from the version of Wasmer for use with cache versioning +pub const WASMER_VERSION_HASH: &'static str = + include_str!(concat!(env!("OUT_DIR"), "/wasmer_version_hash.txt")); diff --git a/lib/runtime-core/src/codegen.rs b/lib/runtime-core/src/codegen.rs new file mode 100644 index 000000000..b61ce58bb --- /dev/null +++ b/lib/runtime-core/src/codegen.rs @@ -0,0 +1,264 @@ +use crate::{ + backend::RunnableModule, + backend::{Backend, CacheGen, Compiler, CompilerConfig, Token}, + cache::{Artifact, Error as CacheError}, + error::{CompileError, CompileResult}, + module::{ModuleInfo, ModuleInner}, + structures::Map, + types::{FuncIndex, FuncSig, SigIndex}, +}; +use smallvec::SmallVec; +use std::fmt; +use std::fmt::Debug; +use std::marker::PhantomData; +use wasmparser::{Operator, Type as WpType}; + +#[derive(Debug)] +pub enum Event<'a, 'b> { + Internal(InternalEvent), + Wasm(&'b Operator<'a>), +} + +pub enum InternalEvent { + FunctionBegin(u32), + FunctionEnd, + Breakpoint(Box), + SetInternal(u32), + GetInternal(u32), +} + +impl fmt::Debug for InternalEvent { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + InternalEvent::FunctionBegin(_) => write!(f, "FunctionBegin"), + InternalEvent::FunctionEnd => write!(f, "FunctionEnd"), + InternalEvent::Breakpoint(_) => write!(f, "Breakpoint"), + InternalEvent::SetInternal(_) => write!(f, "SetInternal"), + InternalEvent::GetInternal(_) => write!(f, "GetInternal"), + } + } +} + +pub struct BkptInfo {} + +pub trait ModuleCodeGenerator, RM: RunnableModule, E: Debug> { + fn new() -> Self; + fn backend_id() -> Backend; + fn check_precondition(&mut self, module_info: &ModuleInfo) -> Result<(), E>; + + /// Creates a new function and returns the function-scope code generator for it. + fn next_function(&mut self) -> Result<&mut FCG, E>; + fn finalize(self, module_info: &ModuleInfo) -> Result<(RM, Box), E>; + fn feed_signatures(&mut self, signatures: Map) -> Result<(), E>; + + /// Sets function signatures. + fn feed_function_signatures(&mut self, assoc: Map) -> Result<(), E>; + + /// Adds an import function. + fn feed_import_function(&mut self) -> Result<(), E>; + + unsafe fn from_cache(cache: Artifact, _: Token) -> Result; +} + +pub struct StreamingCompiler< + MCG: ModuleCodeGenerator, + FCG: FunctionCodeGenerator, + RM: RunnableModule + 'static, + E: Debug, + CGEN: Fn() -> MiddlewareChain, +> { + middleware_chain_generator: CGEN, + _phantom_mcg: PhantomData, + _phantom_fcg: PhantomData, + _phantom_rm: PhantomData, + _phantom_e: PhantomData, +} + +pub struct SimpleStreamingCompilerGen< + MCG: ModuleCodeGenerator, + FCG: FunctionCodeGenerator, + RM: RunnableModule + 'static, + E: Debug, +> { + _phantom_mcg: PhantomData, + _phantom_fcg: PhantomData, + _phantom_rm: PhantomData, + _phantom_e: PhantomData, +} + +impl< + MCG: ModuleCodeGenerator, + FCG: FunctionCodeGenerator, + RM: RunnableModule + 'static, + E: Debug, + > SimpleStreamingCompilerGen +{ + pub fn new() -> StreamingCompiler MiddlewareChain> { + StreamingCompiler::new(|| MiddlewareChain::new()) + } +} + +impl< + MCG: ModuleCodeGenerator, + FCG: FunctionCodeGenerator, + RM: RunnableModule + 'static, + E: Debug, + CGEN: Fn() -> MiddlewareChain, + > StreamingCompiler +{ + pub fn new(chain_gen: CGEN) -> Self { + Self { + middleware_chain_generator: chain_gen, + _phantom_mcg: PhantomData, + _phantom_fcg: PhantomData, + _phantom_rm: PhantomData, + _phantom_e: PhantomData, + } + } +} + +impl< + MCG: ModuleCodeGenerator, + FCG: FunctionCodeGenerator, + RM: RunnableModule + 'static, + E: Debug, + CGEN: Fn() -> MiddlewareChain, + > Compiler for StreamingCompiler +{ + fn compile( + &self, + wasm: &[u8], + compiler_config: CompilerConfig, + _: Token, + ) -> CompileResult { + let mut mcg = MCG::new(); + let mut chain = (self.middleware_chain_generator)(); + let info = crate::parse::read_module( + wasm, + MCG::backend_id(), + &mut mcg, + &mut chain, + &compiler_config, + )?; + let (exec_context, cache_gen) = + mcg.finalize(&info) + .map_err(|x| CompileError::InternalError { + msg: format!("{:?}", x), + })?; + Ok(ModuleInner { + cache_gen, + runnable_module: Box::new(exec_context), + info, + }) + } + + unsafe fn from_cache( + &self, + artifact: Artifact, + token: Token, + ) -> Result { + MCG::from_cache(artifact, token) + } +} + +pub struct EventSink<'a, 'b> { + buffer: SmallVec<[Event<'a, 'b>; 2]>, +} + +impl<'a, 'b> EventSink<'a, 'b> { + pub fn push(&mut self, ev: Event<'a, 'b>) { + self.buffer.push(ev); + } +} + +pub struct MiddlewareChain { + chain: Vec>, +} + +impl MiddlewareChain { + pub fn new() -> MiddlewareChain { + MiddlewareChain { chain: vec![] } + } + + pub fn push(&mut self, m: M) { + self.chain.push(Box::new(m)); + } + + pub(crate) fn run>( + &mut self, + fcg: Option<&mut FCG>, + ev: Event, + module_info: &ModuleInfo, + ) -> Result<(), String> { + let mut sink = EventSink { + buffer: SmallVec::new(), + }; + sink.push(ev); + for m in &mut self.chain { + let prev: SmallVec<[Event; 2]> = sink.buffer.drain().collect(); + for ev in prev { + m.feed_event(ev, module_info, &mut sink)?; + } + } + if let Some(fcg) = fcg { + for ev in sink.buffer { + fcg.feed_event(ev, module_info) + .map_err(|x| format!("{:?}", x))?; + } + } + + Ok(()) + } +} + +pub trait FunctionMiddleware { + type Error: Debug; + fn feed_event<'a, 'b: 'a>( + &mut self, + op: Event<'a, 'b>, + module_info: &ModuleInfo, + sink: &mut EventSink<'a, 'b>, + ) -> Result<(), Self::Error>; +} + +pub(crate) trait GenericFunctionMiddleware { + fn feed_event<'a, 'b: 'a>( + &mut self, + op: Event<'a, 'b>, + module_info: &ModuleInfo, + sink: &mut EventSink<'a, 'b>, + ) -> Result<(), String>; +} + +impl> GenericFunctionMiddleware for T { + fn feed_event<'a, 'b: 'a>( + &mut self, + op: Event<'a, 'b>, + module_info: &ModuleInfo, + sink: &mut EventSink<'a, 'b>, + ) -> Result<(), String> { + ::feed_event(self, op, module_info, sink) + .map_err(|x| format!("{:?}", x)) + } +} + +/// The function-scope code generator trait. +pub trait FunctionCodeGenerator { + /// Sets the return type. + fn feed_return(&mut self, ty: WpType) -> Result<(), E>; + + /// Adds a parameter to the function. + fn feed_param(&mut self, ty: WpType) -> Result<(), E>; + + /// Adds `n` locals to the function. + fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), E>; + + /// Called before the first call to `feed_opcode`. + fn begin_body(&mut self, module_info: &ModuleInfo) -> Result<(), E>; + + /// Called for each operator. + fn feed_event(&mut self, op: Event, module_info: &ModuleInfo) -> Result<(), E>; + + /// Finalizes the function. + fn finalize(&mut self) -> Result<(), E>; +} diff --git a/lib/runtime-core/src/error.rs b/lib/runtime-core/src/error.rs index 9e99c2833..fb25acdb8 100644 --- a/lib/runtime-core/src/error.rs +++ b/lib/runtime-core/src/error.rs @@ -1,4 +1,4 @@ -use crate::types::{FuncSig, GlobalDescriptor, MemoryDescriptor, TableDescriptor, Type, Value}; +use crate::types::{FuncSig, GlobalDescriptor, MemoryDescriptor, TableDescriptor, Type}; use core::borrow::Borrow; use std::any::Any; @@ -8,6 +8,7 @@ pub type LinkResult = std::result::Result>; pub type RuntimeResult = std::result::Result; pub type CallResult = std::result::Result; pub type ResolveResult = std::result::Result; +pub type ParseResult = std::result::Result; /// This is returned when the chosen compiler is unable to /// successfully compile the provided webassembly module into @@ -118,11 +119,9 @@ impl std::error::Error for LinkError {} /// The main way to do this is `Instance.call`. /// /// Comparing two `RuntimeError`s always evaluates to false. -#[derive(Debug)] pub enum RuntimeError { Trap { msg: Box }, - Exception { data: Box<[Value]> }, - Panic { data: Box }, + Error { data: Box }, } impl PartialEq for RuntimeError { @@ -137,14 +136,25 @@ impl std::fmt::Display for RuntimeError { RuntimeError::Trap { ref msg } => { write!(f, "WebAssembly trap occured during runtime: {}", msg) } - RuntimeError::Exception { ref data } => { - write!(f, "Uncaught WebAssembly exception: {:?}", data) + RuntimeError::Error { data } => { + if let Some(s) = data.downcast_ref::() { + write!(f, "\"{}\"", s) + } else if let Some(s) = data.downcast_ref::<&str>() { + write!(f, "\"{}\"", s) + } else { + write!(f, "unknown error") + } } - RuntimeError::Panic { data: _ } => write!(f, "User-defined \"panic\""), } } } +impl std::fmt::Debug for RuntimeError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self) + } +} + impl std::error::Error for RuntimeError {} /// This error type is produced by resolving a wasm function @@ -196,7 +206,6 @@ impl std::error::Error for ResolveError {} /// be the `CallError::Runtime(RuntimeError)` variant. /// /// Comparing two `CallError`s always evaluates to false. -#[derive(Debug)] pub enum CallError { Resolve(ResolveError), Runtime(RuntimeError), @@ -217,6 +226,15 @@ impl std::fmt::Display for CallError { } } +impl std::fmt::Debug for CallError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + CallError::Resolve(resolve_err) => write!(f, "ResolveError: {:?}", resolve_err), + CallError::Runtime(runtime_err) => write!(f, "RuntimeError: {:?}", runtime_err), + } + } +} + impl std::error::Error for CallError {} /// This error type is produced when creating something, @@ -445,3 +463,14 @@ impl Into for MemoryProtectionError { GrowError::CouldNotProtectMemory(self) } } + +#[derive(Debug)] +pub enum ParseError { + BinaryReadError, +} + +impl From for ParseError { + fn from(_: wasmparser::BinaryReaderError) -> Self { + ParseError::BinaryReadError + } +} diff --git a/lib/runtime-core/src/export.rs b/lib/runtime-core/src/export.rs index 0a2c09136..81e0eae92 100644 --- a/lib/runtime-core/src/export.rs +++ b/lib/runtime-core/src/export.rs @@ -40,13 +40,13 @@ impl FuncPointer { } pub struct ExportIter<'a> { - inner: &'a mut InstanceInner, + inner: &'a InstanceInner, iter: hash_map::Iter<'a, String, ExportIndex>, module: &'a ModuleInner, } impl<'a> ExportIter<'a> { - pub(crate) fn new(module: &'a ModuleInner, inner: &'a mut InstanceInner) -> Self { + pub(crate) fn new(module: &'a ModuleInner, inner: &'a InstanceInner) -> Self { Self { inner, iter: module.info.exports.iter(), diff --git a/lib/runtime-core/src/import.rs b/lib/runtime-core/src/import.rs index 59d9da555..14465deed 100644 --- a/lib/runtime-core/src/import.rs +++ b/lib/runtime-core/src/import.rs @@ -1,12 +1,16 @@ use crate::export::Export; use hashbrown::{hash_map::Entry, HashMap}; +use std::collections::VecDeque; use std::{ cell::{Ref, RefCell}, + ffi::c_void, rc::Rc, }; pub trait LikeNamespace { fn get_export(&self, name: &str) -> Option; + fn get_exports(&self) -> Vec<(String, Export)>; + fn maybe_insert(&mut self, name: &str, export: Export) -> Option<()>; } pub trait IsExport { @@ -42,6 +46,7 @@ impl IsExport for Export { /// ``` pub struct ImportObject { map: Rc>>>, + state_creator: Option (*mut c_void, fn(*mut c_void))>>, } impl ImportObject { @@ -49,9 +54,24 @@ impl ImportObject { pub fn new() -> Self { Self { map: Rc::new(RefCell::new(HashMap::new())), + state_creator: None, } } + pub fn new_with_data(state_creator: F) -> Self + where + F: Fn() -> (*mut c_void, fn(*mut c_void)) + 'static, + { + Self { + map: Rc::new(RefCell::new(HashMap::new())), + state_creator: Some(Rc::new(state_creator)), + } + } + + pub(crate) fn call_state_creator(&self) -> Option<(*mut c_void, fn(*mut c_void))> { + self.state_creator.as_ref().map(|state_gen| state_gen()) + } + /// Register anything that implements `LikeNamespace` as a namespace. /// /// # Usage: @@ -95,6 +115,54 @@ impl ImportObject { pub fn clone_ref(&self) -> Self { Self { map: Rc::clone(&self.map), + state_creator: self.state_creator.clone(), + } + } + + fn get_objects(&self) -> VecDeque<(String, String, Export)> { + let mut out = VecDeque::new(); + for (name, ns) in self.map.borrow().iter() { + for (id, exp) in ns.get_exports() { + out.push_back((name.clone(), id, exp)); + } + } + out + } +} + +pub struct ImportObjectIterator { + elements: VecDeque<(String, String, Export)>, +} + +impl Iterator for ImportObjectIterator { + type Item = (String, String, Export); + fn next(&mut self) -> Option { + self.elements.pop_front() + } +} + +impl IntoIterator for ImportObject { + type IntoIter = ImportObjectIterator; + type Item = (String, String, Export); + + fn into_iter(self) -> Self::IntoIter { + ImportObjectIterator { + elements: self.get_objects(), + } + } +} + +impl Extend<(String, String, Export)> for ImportObject { + fn extend>(&mut self, iter: T) { + let mut map = self.map.borrow_mut(); + for (ns, id, exp) in iter.into_iter() { + if let Some(like_ns) = map.get_mut(&ns) { + like_ns.maybe_insert(&id, exp); + } else { + let mut new_ns = Namespace::new(); + new_ns.insert(id, exp); + map.insert(ns, Box::new(new_ns)); + } } } } @@ -123,4 +191,76 @@ impl LikeNamespace for Namespace { fn get_export(&self, name: &str) -> Option { self.map.get(name).map(|is_export| is_export.to_export()) } + + fn get_exports(&self) -> Vec<(String, Export)> { + self.map + .iter() + .map(|(k, v)| (k.clone(), v.to_export())) + .collect() + } + + fn maybe_insert(&mut self, name: &str, export: Export) -> Option<()> { + self.map.insert(name.to_owned(), Box::new(export)); + Some(()) + } +} + +#[cfg(test)] +mod test { + use crate::export::Export; + use crate::global::Global; + use crate::types::Value; + + #[test] + fn extending_works() { + let mut imports1 = imports! { + "dog" => { + "happy" => Global::new(Value::I32(0)), + }, + }; + + let imports2 = imports! { + "dog" => { + "small" => Global::new(Value::I32(2)), + }, + "cat" => { + "small" => Global::new(Value::I32(3)), + }, + }; + + imports1.extend(imports2); + + let cat_ns = imports1.get_namespace("cat").unwrap(); + assert!(cat_ns.get_export("small").is_some()); + + let dog_ns = imports1.get_namespace("dog").unwrap(); + assert!(dog_ns.get_export("happy").is_some()); + assert!(dog_ns.get_export("small").is_some()); + } + + #[test] + fn extending_conflict_overwrites() { + let mut imports1 = imports! { + "dog" => { + "happy" => Global::new(Value::I32(0)), + }, + }; + + let imports2 = imports! { + "dog" => { + "happy" => Global::new(Value::I32(4)), + }, + }; + + imports1.extend(imports2); + let dog_ns = imports1.get_namespace("dog").unwrap(); + + assert!( + if let Export::Global(happy_dog_global) = dog_ns.get_export("happy").unwrap() { + happy_dog_global.get() == Value::I32(4) + } else { + false + } + ); + } } diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index 3dee6ee50..14d7a0146 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -1,19 +1,20 @@ use crate::{ - backend::Token, + backend::RunnableModule, backing::{ImportBacking, LocalBacking}, - error::{CallError, CallResult, ResolveError, ResolveResult, Result}, + error::{CallError, CallResult, ResolveError, ResolveResult, Result, RuntimeError}, export::{Context, Export, ExportIter, FuncPointer}, global::Global, import::{ImportObject, LikeNamespace}, memory::Memory, - module::{ExportIndex, Module, ModuleInner}, + module::{ExportIndex, Module, ModuleInfo, ModuleInner}, sig_registry::SigRegistry, table::Table, - typed_func::{Func, Safe, WasmTypeList}, - types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Value}, + typed_func::{Func, Wasm, WasmTrapInfo, WasmTypeList}, + types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Type, Value}, vm, }; -use std::{mem, sync::Arc}; +use smallvec::{smallvec, SmallVec}; +use std::{mem, ptr::NonNull, sync::Arc}; pub(crate) struct InstanceInner { #[allow(dead_code)] @@ -63,7 +64,16 @@ impl Instance { // Initialize the vm::Ctx in-place after the backing // has been boxed. unsafe { - *inner.vmctx = vm::Ctx::new(&mut inner.backing, &mut inner.import_backing, &module) + *inner.vmctx = match imports.call_state_creator() { + Some((data, dtor)) => vm::Ctx::new_with_data( + &mut inner.backing, + &mut inner.import_backing, + &module, + data, + dtor, + ), + None => vm::Ctx::new(&mut inner.backing, &mut inner.import_backing, &module), + }; }; let instance = Instance { @@ -73,7 +83,45 @@ impl Instance { }; if let Some(start_index) = instance.module.info.start_func { - instance.call_with_index(start_index, &[])?; + // We know that the start function takes no arguments and returns no values. + // Therefore, we can call it without doing any signature checking, etc. + + let func_ptr = match start_index.local_or_import(&instance.module.info) { + LocalOrImport::Local(local_func_index) => instance + .module + .runnable_module + .get_func(&instance.module.info, local_func_index) + .unwrap(), + LocalOrImport::Import(import_func_index) => NonNull::new( + instance.inner.import_backing.vm_functions[import_func_index].func as *mut _, + ) + .unwrap(), + }; + + let ctx_ptr = match start_index.local_or_import(&instance.module.info) { + LocalOrImport::Local(_) => instance.inner.vmctx, + LocalOrImport::Import(imported_func_index) => { + instance.inner.import_backing.vm_functions[imported_func_index].vmctx + } + }; + + let sig_index = *instance + .module + .info + .func_assoc + .get(start_index) + .expect("broken invariant, incorrect func index"); + + let wasm_trampoline = instance + .module + .runnable_module + .get_trampoline(&instance.module.info, sig_index) + .expect("wasm trampoline"); + + let start_func: Func<(), (), Wasm> = + unsafe { Func::from_raw_parts(wasm_trampoline, func_ptr, ctx_ptr) }; + + start_func.call()?; } Ok(instance) @@ -98,7 +146,7 @@ impl Instance { /// # Ok(()) /// # } /// ``` - pub fn func(&self, name: &str) -> ResolveResult> + pub fn func(&self, name: &str) -> ResolveResult> where Args: WasmTypeList, Rets: WasmTypeList, @@ -136,20 +184,26 @@ impl Instance { } }; + let func_wasm_inner = self + .module + .runnable_module + .get_trampoline(&self.module.info, sig_index) + .unwrap(); + let func_ptr = match func_index.local_or_import(&self.module.info) { LocalOrImport::Local(local_func_index) => self .module - .func_resolver - .get(&self.module, local_func_index) - .unwrap() - .as_ptr(), - LocalOrImport::Import(import_func_index) => { - self.inner.import_backing.vm_functions[import_func_index].func - } + .runnable_module + .get_func(&self.module.info, local_func_index) + .unwrap(), + LocalOrImport::Import(import_func_index) => NonNull::new( + self.inner.import_backing.vm_functions[import_func_index].func as *mut _, + ) + .unwrap(), }; - let typed_func: Func = - unsafe { Func::new_from_ptr(func_ptr as _, ctx) }; + let typed_func: Func = + unsafe { Func::from_raw_parts(func_wasm_inner, func_ptr, ctx) }; Ok(typed_func) } else { @@ -230,7 +284,7 @@ impl Instance { /// # Ok(()) /// # } /// ``` - pub fn call(&self, name: &str, args: &[Value]) -> CallResult> { + pub fn call(&self, name: &str, params: &[Value]) -> CallResult> { let export_index = self.module .info @@ -249,7 +303,19 @@ impl Instance { .into()); }; - self.call_with_index(func_index, args) + let mut results = Vec::new(); + + call_func_with_index( + &self.module.info, + &*self.module.runnable_module, + &self.inner.import_backing, + self.inner.vmctx, + func_index, + params, + &mut results, + )?; + + Ok(results) } /// Returns an immutable reference to the @@ -270,8 +336,8 @@ impl Instance { /// Returns an iterator over all of the items /// exported from this instance. - pub fn exports(&mut self) -> ExportIter { - ExportIter::new(&self.module, &mut self.inner) + pub fn exports(&self) -> ExportIter { + ExportIter::new(&self.module, &self.inner) } /// The module used to instantiate this Instance. @@ -280,45 +346,6 @@ impl Instance { } } -impl Instance { - fn call_with_index(&self, func_index: FuncIndex, args: &[Value]) -> CallResult> { - let sig_index = *self - .module - .info - .func_assoc - .get(func_index) - .expect("broken invariant, incorrect func index"); - let signature = &self.module.info.signatures[sig_index]; - - if !signature.check_param_value_types(args) { - Err(ResolveError::Signature { - expected: signature.clone(), - found: args.iter().map(|val| val.ty()).collect(), - })? - } - - 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 - } - }; - - let token = Token::generate(); - - let returns = self.module.protected_caller.call( - &self.module, - func_index, - args, - &self.inner.import_backing, - vmctx, - token, - )?; - - Ok(returns) - } -} - impl InstanceInner { pub(crate) fn get_export_from_index( &self, @@ -367,8 +394,8 @@ impl InstanceInner { 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) + .runnable_module + .get_func(&module.info, local_func_index) .expect("broken invariant, func resolver not synced with module.exports") .cast() .as_ptr() as *const _, @@ -427,6 +454,142 @@ impl LikeNamespace for Instance { Some(self.inner.get_export_from_index(&self.module, export_index)) } + + fn get_exports(&self) -> Vec<(String, Export)> { + unimplemented!("Use the exports method instead"); + } + + fn maybe_insert(&mut self, _name: &str, _export: Export) -> Option<()> { + None + } +} + +#[must_use] +fn call_func_with_index( + info: &ModuleInfo, + runnable: &dyn RunnableModule, + import_backing: &ImportBacking, + local_ctx: *mut vm::Ctx, + func_index: FuncIndex, + args: &[Value], + rets: &mut Vec, +) -> CallResult<()> { + rets.clear(); + + let sig_index = *info + .func_assoc + .get(func_index) + .expect("broken invariant, incorrect func index"); + + let signature = &info.signatures[sig_index]; + let num_results = signature.returns().len(); + rets.reserve(num_results); + + if !signature.check_param_value_types(args) { + Err(ResolveError::Signature { + expected: signature.clone(), + found: args.iter().map(|val| val.ty()).collect(), + })? + } + + let func_ptr = match func_index.local_or_import(info) { + LocalOrImport::Local(local_func_index) => { + runnable.get_func(info, local_func_index).unwrap() + } + LocalOrImport::Import(import_func_index) => { + NonNull::new(import_backing.vm_functions[import_func_index].func as *mut _).unwrap() + } + }; + + let ctx_ptr = match func_index.local_or_import(info) { + LocalOrImport::Local(_) => local_ctx, + LocalOrImport::Import(imported_func_index) => { + import_backing.vm_functions[imported_func_index].vmctx + } + }; + + let raw_args: SmallVec<[u64; 8]> = args + .iter() + .map(|v| match v { + Value::I32(i) => *i as u64, + Value::I64(i) => *i as u64, + Value::F32(f) => f.to_bits() as u64, + Value::F64(f) => f.to_bits(), + }) + .collect(); + + let Wasm { + trampoline, + invoke, + invoke_env, + } = runnable + .get_trampoline(info, sig_index) + .expect("wasm trampoline"); + + let run_wasm = |result_space: *mut u64| unsafe { + let mut trap_info = WasmTrapInfo::Unknown; + let mut user_error = None; + + let success = invoke( + trampoline, + ctx_ptr, + func_ptr, + raw_args.as_ptr(), + result_space, + &mut trap_info, + &mut user_error, + invoke_env, + ); + + if success { + Ok(()) + } else { + if let Some(data) = user_error { + Err(RuntimeError::Error { data }) + } else { + Err(RuntimeError::Trap { + msg: trap_info.to_string().into(), + }) + } + } + }; + + let raw_to_value = |raw, ty| match ty { + Type::I32 => Value::I32(raw as i32), + Type::I64 => Value::I64(raw as i64), + Type::F32 => Value::F32(f32::from_bits(raw as u32)), + Type::F64 => Value::F64(f64::from_bits(raw)), + }; + + match signature.returns() { + &[] => { + run_wasm(0 as *mut u64)?; + Ok(()) + } + &[ty] => { + let mut result = 0u64; + + run_wasm(&mut result)?; + + rets.push(raw_to_value(result, ty)); + + Ok(()) + } + result_tys @ _ => { + let mut results: SmallVec<[u64; 8]> = smallvec![0; num_results]; + + run_wasm(results.as_mut_ptr())?; + + rets.extend( + results + .iter() + .zip(result_tys.iter()) + .map(|(&raw, &ty)| raw_to_value(raw, ty)), + ); + + Ok(()) + } + } } /// A representation of an exported WebAssembly function. @@ -461,32 +624,19 @@ impl<'a> DynFunc<'a> { /// # } /// ``` pub fn call(&self, params: &[Value]) -> CallResult> { - if !self.signature.check_param_value_types(params) { - Err(ResolveError::Signature { - expected: (*self.signature).clone(), - found: params.iter().map(|val| val.ty()).collect(), - })? - } + let mut results = Vec::new(); - 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 - } - }; - - let token = Token::generate(); - - let returns = self.module.protected_caller.call( - &self.module, + call_func_with_index( + &self.module.info, + &*self.module.runnable_module, + &self.instance_inner.import_backing, + self.instance_inner.vmctx, self.func_index, params, - &self.instance_inner.import_backing, - vmctx, - token, + &mut results, )?; - Ok(returns) + Ok(results) } pub fn signature(&self) -> &FuncSig { @@ -497,8 +647,8 @@ impl<'a> DynFunc<'a> { match self.func_index.local_or_import(&self.module.info) { LocalOrImport::Local(local_func_index) => self .module - .func_resolver - .get(self.module, local_func_index) + .runnable_module + .get_func(&self.module.info, local_func_index) .unwrap() .as_ptr(), LocalOrImport::Import(import_func_index) => { @@ -507,10 +657,3 @@ impl<'a> DynFunc<'a> { } } } - -#[doc(hidden)] -impl Instance { - pub fn memory_offset_addr(&self, _: u32, _: usize) -> *const u8 { - unimplemented!() - } -} diff --git a/lib/runtime-core/src/lib.rs b/lib/runtime-core/src/lib.rs index 663e17534..da9144a2b 100644 --- a/lib/runtime-core/src/lib.rs +++ b/lib/runtime-core/src/lib.rs @@ -1,3 +1,6 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] +#![cfg_attr(nightly, feature(unwind_attributes))] + #[cfg(test)] #[macro_use] extern crate field_offset; @@ -12,6 +15,7 @@ pub mod backend; mod backing; pub mod cache; +pub mod codegen; pub mod error; pub mod export; pub mod global; @@ -19,11 +23,12 @@ pub mod import; pub mod instance; pub mod memory; pub mod module; +pub mod parse; mod sig_registry; pub mod structures; mod sys; pub mod table; -mod typed_func; +pub mod typed_func; pub mod types; pub mod units; pub mod vm; @@ -36,7 +41,7 @@ pub use self::error::Result; #[doc(inline)] pub use self::import::IsExport; #[doc(inline)] -pub use self::instance::Instance; +pub use self::instance::{DynFunc, Instance}; #[doc(inline)] pub use self::module::Module; #[doc(inline)] @@ -68,7 +73,24 @@ pub fn compile_with( ) -> CompileResult { let token = backend::Token::generate(); compiler - .compile(wasm, token) + .compile(wasm, Default::default(), token) + .map(|mut inner| { + let inner_info: &mut crate::module::ModuleInfo = &mut inner.info; + inner_info.import_custom_sections(wasm).unwrap(); + module::Module::new(Arc::new(inner)) + }) +} + +/// The same as `compile_with` but changes the compiler behavior +/// with the values in the `CompilerConfig` +pub fn compile_with_config( + wasm: &[u8], + compiler: &dyn backend::Compiler, + compiler_config: backend::CompilerConfig, +) -> CompileResult { + let token = backend::Token::generate(); + compiler + .compile(wasm, compiler_config, token) .map(|inner| module::Module::new(Arc::new(inner))) } @@ -76,13 +98,18 @@ pub fn compile_with( /// WebAssembly specification. Returns `true` if validation /// succeeded, `false` if validation failed. pub fn validate(wasm: &[u8]) -> bool { + validate_and_report_errors(wasm).is_ok() +} + +/// The same as `validate` but with an Error message on failure +pub fn validate_and_report_errors(wasm: &[u8]) -> ::std::result::Result<(), String> { use wasmparser::WasmDecoder; let mut parser = wasmparser::ValidatingParser::new(wasm, None); loop { let state = parser.read(); match *state { - wasmparser::ParserState::EndWasm => break true, - wasmparser::ParserState::Error(_) => break false, + wasmparser::ParserState::EndWasm => break Ok(()), + wasmparser::ParserState::Error(e) => break Err(format!("{}", e)), _ => {} } } diff --git a/lib/runtime-core/src/macros.rs b/lib/runtime-core/src/macros.rs index a7bdff1b0..edda29366 100644 --- a/lib/runtime-core/src/macros.rs +++ b/lib/runtime-core/src/macros.rs @@ -1,8 +1,14 @@ #[macro_export] #[cfg(feature = "debug")] macro_rules! debug { - ($fmt:expr) => (println!(concat!("wasmer-runtime(:{})::", $fmt), line!())); - ($fmt:expr, $($arg:tt)*) => (println!(concat!("wasmer-runtime(:{})::", $fmt, "\n"), line!(), $($arg)*)); + ($fmt:expr) => (println!(concat!("[{}] wasmer-runtime(:{}) ", $fmt), { + let time = ::std::time::SystemTime::now().duration_since(::std::time::UNIX_EPOCH).expect("Can't get time"); + format!("{}.{:03}", time.as_secs(), time.subsec_millis()) + }, line!())); + ($fmt:expr, $($arg:tt)*) => (println!(concat!("[{}] wasmer-runtime(:{}) ", $fmt, "\n"), { + let time = ::std::time::SystemTime::now().duration_since(::std::time::UNIX_EPOCH).expect("Can't get time"); + format!("{}.{:03}", time.as_secs(), time.subsec_millis()) + }, line!(), $($arg)*)); } #[macro_export] @@ -38,6 +44,13 @@ macro_rules! func { /// }, /// }; /// +/// let imports_with_state = imports! { +/// || (0 as _, |_a| {}), +/// "env" => { +/// "foo" => func!(foo), +/// }, +/// }; +/// /// fn foo(_: &mut Ctx, n: i32) -> i32 { /// n /// } @@ -57,6 +70,21 @@ macro_rules! imports { import_object.register($ns_name, ns); })* + import_object + }}; + ($state_gen:expr, $( $ns_name:expr => $ns:tt, )* ) => {{ + use $crate::{ + import::{ImportObject, Namespace}, + }; + + let mut import_object = ImportObject::new_with_data($state_gen); + + $({ + let ns = $crate::__imports_internal!($ns); + + import_object.register($ns_name, ns); + })* + import_object }}; } @@ -75,3 +103,15 @@ macro_rules! __imports_internal { $ns }; } + +#[macro_export] +#[doc(hidden)] +macro_rules! namespace { + ( $( $imp_name:expr => $import_item:expr, )* ) => {{ + let mut ns = $crate::import::Namespace::new(); + $( + ns.insert($imp_name, $import_item); + )* + ns + }}; +} diff --git a/lib/runtime-core/src/memory/atomic.rs b/lib/runtime-core/src/memory/atomic.rs index f4ed3d3d0..f14415def 100644 --- a/lib/runtime-core/src/memory/atomic.rs +++ b/lib/runtime-core/src/memory/atomic.rs @@ -52,6 +52,7 @@ macro_rules! intcast { } intcast! { u8 i8 u16 i16 u32 i32 u64 i64 } +#[repr(transparent)] pub struct Atomic { v: UnsafeCell>, } diff --git a/lib/runtime-core/src/memory/mod.rs b/lib/runtime-core/src/memory/mod.rs index 4a9dccba7..6f532b6d4 100644 --- a/lib/runtime-core/src/memory/mod.rs +++ b/lib/runtime-core/src/memory/mod.rs @@ -284,6 +284,7 @@ impl Clone for UnsharedMemory { } pub struct SharedMemory { + #[allow(dead_code)] desc: MemoryDescriptor, } diff --git a/lib/runtime-core/src/module.rs b/lib/runtime-core/src/module.rs index 212c197c3..1ffbc71fc 100644 --- a/lib/runtime-core/src/module.rs +++ b/lib/runtime-core/src/module.rs @@ -1,10 +1,9 @@ use crate::{ - backend::{Backend, FuncResolver, ProtectedCaller}, + backend::{Backend, RunnableModule}, cache::{Artifact, Error as CacheError}, error, import::ImportObject, structures::{Map, TypedIndex}, - typed_func::EARLY_TRAPPER, types::{ FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit, ImportedFuncIndex, ImportedGlobalIndex, ImportedMemoryIndex, ImportedTableIndex, Initializer, @@ -22,9 +21,7 @@ use std::sync::Arc; /// This is used to instantiate a new WebAssembly module. #[doc(hidden)] pub struct ModuleInner { - pub func_resolver: Box, - pub protected_caller: Box, - + pub runnable_module: Box, pub cache_gen: Box, pub info: ModuleInfo, @@ -56,6 +53,29 @@ pub struct ModuleInfo { pub namespace_table: StringTable, pub name_table: StringTable, + + /// Symbol information from emscripten + pub em_symbol_map: Option>, + + pub custom_sections: HashMap>, +} + +impl ModuleInfo { + pub fn import_custom_sections(&mut self, wasm: &[u8]) -> crate::error::ParseResult<()> { + let mut parser = wasmparser::ModuleReader::new(wasm)?; + while !parser.eof() { + let section = parser.read()?; + if let wasmparser::SectionCode::Custom { name, kind: _ } = section.code { + let mut reader = section.get_binary_reader(); + let len = reader.bytes_remaining(); + let bytes = reader.read_bytes(len)?; + let data = bytes.to_vec(); + let name = name.to_string(); + self.custom_sections.insert(name, data); + } + } + Ok(()) + } } /// A compiled WebAssembly module. @@ -71,10 +91,6 @@ pub struct Module { impl Module { pub(crate) fn new(inner: Arc) -> Self { - unsafe { - EARLY_TRAPPER - .with(|ucell| *ucell.get() = Some(inner.protected_caller.get_early_trapper())); - } Module { inner } } @@ -105,8 +121,12 @@ impl Module { } 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)) + let (backend_metadata, code) = self.inner.cache_gen.generate_cache()?; + Ok(Artifact::from_parts( + Box::new(self.inner.info.clone()), + backend_metadata, + code, + )) } pub fn info(&self) -> &ModuleInfo { diff --git a/lib/runtime-core/src/parse.rs b/lib/runtime-core/src/parse.rs new file mode 100644 index 000000000..e7e80fc29 --- /dev/null +++ b/lib/runtime-core/src/parse.rs @@ -0,0 +1,426 @@ +use crate::codegen::*; +use crate::{ + backend::{Backend, CompilerConfig, RunnableModule}, + error::CompileError, + 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 hashbrown::HashMap; +use std::fmt::Debug; +use wasmparser::{ + BinaryReaderError, ExternalKind, FuncType, ImportSectionEntryType, Operator, Type as WpType, + WasmDecoder, +}; + +#[derive(Debug)] +pub enum LoadError { + Parse(BinaryReaderError), + Codegen(String), +} + +impl From for CompileError { + fn from(other: LoadError) -> CompileError { + CompileError::InternalError { + msg: format!("{:?}", other), + } + } +} + +impl From for LoadError { + fn from(other: BinaryReaderError) -> LoadError { + LoadError::Parse(other) + } +} + +pub fn read_module< + MCG: ModuleCodeGenerator, + FCG: FunctionCodeGenerator, + RM: RunnableModule, + E: Debug, +>( + wasm: &[u8], + backend: Backend, + mcg: &mut MCG, + middlewares: &mut MiddlewareChain, + compiler_config: &CompilerConfig, +) -> Result { + 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, + + namespace_table: StringTable::new(), + name_table: StringTable::new(), + + em_symbol_map: compiler_config.symbol_map.clone(), + + custom_sections: HashMap::new(), + }; + + let mut parser = wasmparser::ValidatingParser::new( + wasm, + 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, + }), + ); + + let mut namespace_builder = Some(StringTableBuilder::new()); + let mut name_builder = Some(StringTableBuilder::new()); + let mut func_count: usize = ::std::usize::MAX; + + loop { + use wasmparser::ParserState; + let state = parser.read(); + match *state { + ParserState::EndWasm => break Ok(info), + ParserState::Error(err) => Err(LoadError::Parse(err))?, + ParserState::TypeSectionEntry(ref ty) => { + info.signatures.push(func_type_to_func_sig(ty)?); + } + ParserState::ImportSectionEntry { module, field, ty } => { + let namespace_index = namespace_builder.as_mut().unwrap().register(module); + let name_index = name_builder.as_mut().unwrap().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); + mcg.feed_import_function() + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + } + 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: wp_type_to_type(global_ty.content_type)?, + }; + info.imported_globals.push((import_name, global_desc)); + } + } + } + ParserState::FunctionSectionEntry(sigindex) => { + let sigindex = SigIndex::new(sigindex as usize); + info.func_assoc.push(sigindex); + } + ParserState::TableSectionEntry(table_ty) => { + let table_desc = TableDescriptor { + element: ElementType::Anyfunc, + minimum: table_ty.limits.initial, + maximum: table_ty.limits.maximum, + }; + + info.tables.push(table_desc); + } + ParserState::MemorySectionEntry(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); + } + ParserState::ExportSectionEntry { field, kind, index } => { + 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); + } + ParserState::StartSectionEntry(start_index) => { + info.start_func = Some(FuncIndex::new(start_index as usize)); + } + ParserState::BeginFunctionBody { .. } => { + let id = func_count.wrapping_add(1); + func_count = id; + if func_count == 0 { + info.namespace_table = namespace_builder.take().unwrap().finish(); + info.name_table = name_builder.take().unwrap().finish(); + mcg.feed_signatures(info.signatures.clone()) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + mcg.feed_function_signatures(info.func_assoc.clone()) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + mcg.check_precondition(&info) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + } + + let fcg = mcg + .next_function() + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + middlewares + .run( + Some(fcg), + Event::Internal(InternalEvent::FunctionBegin(id as u32)), + &info, + ) + .map_err(|x| LoadError::Codegen(x))?; + + let sig = info + .signatures + .get( + *info + .func_assoc + .get(FuncIndex::new(id as usize + info.imported_functions.len())) + .unwrap(), + ) + .unwrap(); + for ret in sig.returns() { + fcg.feed_return(type_to_wp_type(*ret)) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + } + for param in sig.params() { + fcg.feed_param(type_to_wp_type(*param)) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + } + + let mut body_begun = false; + + loop { + let state = parser.read(); + match *state { + ParserState::Error(err) => return Err(LoadError::Parse(err)), + ParserState::FunctionBodyLocals { ref locals } => { + for &(count, ty) in locals.iter() { + fcg.feed_local(ty, count as usize) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + } + } + ParserState::CodeOperator(ref op) => { + if !body_begun { + body_begun = true; + fcg.begin_body(&info) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + } + middlewares + .run(Some(fcg), Event::Wasm(op), &info) + .map_err(|x| LoadError::Codegen(x))?; + } + ParserState::EndFunctionBody => break, + _ => unreachable!(), + } + } + middlewares + .run( + Some(fcg), + Event::Internal(InternalEvent::FunctionEnd), + &info, + ) + .map_err(|x| LoadError::Codegen(x))?; + fcg.finalize() + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + } + ParserState::BeginActiveElementSectionEntry(table_index) => { + let table_index = TableIndex::new(table_index as usize); + let mut elements: Option> = None; + let mut base: Option = None; + + loop { + let state = parser.read(); + match *state { + ParserState::Error(err) => return Err(LoadError::Parse(err)), + ParserState::InitExpressionOperator(ref op) => { + base = Some(eval_init_expr(op)?) + } + ParserState::ElementSectionEntryBody(ref _elements) => { + elements = Some( + _elements + .iter() + .cloned() + .map(|index| FuncIndex::new(index as usize)) + .collect(), + ); + } + ParserState::BeginInitExpressionBody + | ParserState::EndInitExpressionBody => {} + ParserState::EndElementSectionEntry => break, + _ => unreachable!(), + } + } + + let table_init = TableInitializer { + table_index, + base: base.unwrap(), + elements: elements.unwrap(), + }; + + info.elem_initializers.push(table_init); + } + ParserState::BeginActiveDataSectionEntry(memory_index) => { + let memory_index = MemoryIndex::new(memory_index as usize); + let mut base: Option = None; + let mut data: Vec = vec![]; + + loop { + let state = parser.read(); + match *state { + ParserState::Error(err) => return Err(LoadError::Parse(err)), + ParserState::InitExpressionOperator(ref op) => { + base = Some(eval_init_expr(op)?) + } + ParserState::DataSectionEntryBodyChunk(chunk) => { + data.extend_from_slice(chunk); + } + ParserState::BeginInitExpressionBody + | ParserState::EndInitExpressionBody => {} + ParserState::BeginDataSectionEntryBody(_) + | ParserState::EndDataSectionEntryBody => {} + ParserState::EndDataSectionEntry => break, + _ => unreachable!(), + } + } + + let data_init = DataInitializer { + memory_index, + base: base.unwrap(), + data, + }; + info.data_initializers.push(data_init); + } + ParserState::BeginGlobalSectionEntry(ty) => { + let init = loop { + let state = parser.read(); + match *state { + ParserState::Error(err) => return Err(LoadError::Parse(err)), + ParserState::InitExpressionOperator(ref op) => { + break eval_init_expr(op)?; + } + ParserState::BeginInitExpressionBody => {} + _ => unreachable!(), + } + }; + let desc = GlobalDescriptor { + mutable: ty.mutable, + ty: wp_type_to_type(ty.content_type)?, + }; + + let global_init = GlobalInit { desc, init }; + + info.globals.push(global_init); + } + + _ => {} + } + } +} + +pub fn wp_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, + }); + } + _ => panic!("broken invariant, invalid type"), + }) +} + +pub fn type_to_wp_type(ty: Type) -> WpType { + match ty { + Type::I32 => WpType::I32, + Type::I64 => WpType::I64, + Type::F32 => WpType::F32, + Type::F64 => WpType::F64, + } +} + +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(wp_type_to_type) + .collect::, _>>()?, + func_ty + .returns + .iter() + .cloned() + .map(wp_type_to_type) + .collect::, _>>()?, + )) +} + +fn eval_init_expr(op: &Operator) -> Result { + 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: -1isize as usize, + }); + } + }) +} diff --git a/lib/runtime-core/src/sys/windows/memory.rs b/lib/runtime-core/src/sys/windows/memory.rs index d47388170..888aeff4d 100644 --- a/lib/runtime-core/src/sys/windows/memory.rs +++ b/lib/runtime-core/src/sys/windows/memory.rs @@ -33,7 +33,13 @@ impl Memory { let protect = protection.to_protect_const(); - let ptr = unsafe { VirtualAlloc(ptr::null_mut(), size, MEM_RESERVE, protect) }; + let flags = if protection == Protect::None { + MEM_RESERVE + } else { + MEM_RESERVE | MEM_COMMIT + }; + + let ptr = unsafe { VirtualAlloc(ptr::null_mut(), size, flags, protect) }; if ptr.is_null() { Err("unable to allocate memory".to_string()) @@ -229,3 +235,25 @@ fn round_up_to_page_size(size: usize, page_size: usize) -> usize { fn round_down_to_page_size(size: usize, page_size: usize) -> usize { size & !(page_size - 1) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn clone() { + // these should work + let _ = Memory::with_size_protect(200_000, Protect::Read) + .unwrap() + .clone(); + let _ = Memory::with_size_protect(200_000, Protect::ReadWrite) + .unwrap() + .clone(); + let _ = Memory::with_size_protect(200_000, Protect::ReadExec) + .unwrap() + .clone(); + + // this would cause segmentation fault as uncommited memory with no access + //let _ = Memory::with_size_protect(200_000, Protect::None).unwrap().clone(); + } +} diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 4191f347e..0a9bc8c2f 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -1,29 +1,111 @@ use crate::{ - backend::UserTrapper, error::RuntimeError, export::{Context, Export, FuncPointer}, import::IsExport, - types::{FuncSig, Type, WasmExternType}, - vm::Ctx, + types::{FuncSig, NativeWasmType, Type, WasmExternType}, + vm::{self, Ctx}, +}; +use std::{ + any::Any, + convert::Infallible, + ffi::c_void, + fmt, + marker::PhantomData, + mem, panic, + ptr::{self, NonNull}, + 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); +#[repr(C)] +pub enum WasmTrapInfo { + Unreachable = 0, + IncorrectCallIndirectSignature = 1, + MemoryOutOfBounds = 2, + CallIndirectOOB = 3, + IllegalArithmetic = 4, + Unknown, } -pub trait Safeness {} -pub struct Safe; -pub struct Unsafe; -impl Safeness for Safe {} -impl Safeness for Unsafe {} +impl fmt::Display for WasmTrapInfo { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{}", + match self { + WasmTrapInfo::Unreachable => "unreachable", + WasmTrapInfo::IncorrectCallIndirectSignature => { + "incorrect `call_indirect` signature" + } + WasmTrapInfo::MemoryOutOfBounds => "memory out-of-bounds access", + WasmTrapInfo::CallIndirectOOB => "`call_indirect` out-of-bounds", + WasmTrapInfo::IllegalArithmetic => "illegal arithmetic operation", + WasmTrapInfo::Unknown => "unknown", + } + ) + } +} + +/// This is just an empty trait to constrict that types that +/// can be put into the third/fourth (depending if you include lifetimes) +/// of the `Func` struct. +pub trait Kind {} + +pub type Trampoline = unsafe extern "C" fn(*mut Ctx, NonNull, *const u64, *mut u64); +pub type Invoke = unsafe extern "C" fn( + Trampoline, + *mut Ctx, + NonNull, + *const u64, + *mut u64, + *mut WasmTrapInfo, + *mut Option>, + Option>, +) -> bool; + +/// TODO(lachlan): Naming TBD. +/// This contains the trampoline and invoke functions for a specific signature, +/// as well as the environment that the invoke function may or may not require. +#[derive(Copy, Clone)] +pub struct Wasm { + pub(crate) trampoline: Trampoline, + pub(crate) invoke: Invoke, + pub(crate) invoke_env: Option>, +} + +impl Wasm { + pub unsafe fn from_raw_parts( + trampoline: Trampoline, + invoke: Invoke, + invoke_env: Option>, + ) -> Self { + Self { + trampoline, + invoke, + invoke_env, + } + } +} + +/// This type, as part of the `Func` type signature, represents a function that is created +/// by the host. +pub struct Host(()); +impl Kind for Wasm {} +impl Kind for Host {} pub trait WasmTypeList { type CStruct; + type RetArray: AsMut<[u64]>; + fn from_ret_array(array: Self::RetArray) -> Self; + fn empty_ret_array() -> Self::RetArray; fn from_c_struct(c_struct: Self::CStruct) -> Self; fn into_c_struct(self) -> Self::CStruct; fn types() -> &'static [Type]; - unsafe fn call(self, f: *const (), ctx: *mut Ctx) -> Rets + unsafe fn call( + self, + f: NonNull, + wasm: Wasm, + ctx: *mut Ctx, + ) -> Result where Rets: WasmTypeList; } @@ -33,21 +115,23 @@ where Args: WasmTypeList, Rets: WasmTypeList, { - fn to_raw(&self) -> *const (); + fn to_raw(&self) -> NonNull; } pub trait TrapEarly where Rets: WasmTypeList, { - fn report(self) -> Result>; + type Error: 'static; + fn report(self) -> Result; } impl TrapEarly for Rets where Rets: WasmTypeList, { - fn report(self) -> Result> { + type Error = Infallible; + fn report(self) -> Result { Ok(self) } } @@ -55,10 +139,11 @@ where impl TrapEarly for Result where Rets: WasmTypeList, - E: Any, + E: 'static, { - fn report(self) -> Result> { - self.map_err(|err| Box::new(err) as Box) + type Error = E; + fn report(self) -> Result { + self } } @@ -71,19 +156,25 @@ where // Func::new(f) // } -pub struct Func<'a, Args = (), Rets = (), Safety: Safeness = Safe> { - f: *const (), +pub struct Func<'a, Args = (), Rets = (), Inner: Kind = Wasm> { + inner: Inner, + f: NonNull, ctx: *mut Ctx, - _phantom: PhantomData<(&'a (), Safety, Args, Rets)>, + _phantom: PhantomData<(&'a (), Args, Rets)>, } -impl<'a, Args, Rets> Func<'a, Args, Rets, Safe> +impl<'a, Args, Rets> Func<'a, Args, Rets, Wasm> where Args: WasmTypeList, Rets: WasmTypeList, { - pub(crate) unsafe fn new_from_ptr(f: *const (), ctx: *mut Ctx) -> Func<'a, Args, Rets, Safe> { + pub(crate) unsafe fn from_raw_parts( + inner: Wasm, + f: NonNull, + ctx: *mut Ctx, + ) -> Func<'a, Args, Rets, Wasm> { Func { + inner, f, ctx, _phantom: PhantomData, @@ -91,16 +182,17 @@ where } } -impl<'a, Args, Rets> Func<'a, Args, Rets, Unsafe> +impl<'a, Args, Rets> Func<'a, Args, Rets, Host> where Args: WasmTypeList, Rets: WasmTypeList, { - pub fn new(f: F) -> Func<'a, Args, Rets, Unsafe> + pub fn new(f: F) -> Func<'a, Args, Rets, Host> where F: ExternalFunction, { Func { + inner: Host(()), f: f.to_raw(), ctx: ptr::null_mut(), _phantom: PhantomData, @@ -108,11 +200,11 @@ where } } -impl<'a, Args, Rets, Safety> Func<'a, Args, Rets, Safety> +impl<'a, Args, Rets, Inner> Func<'a, Args, Rets, Inner> where Args: WasmTypeList, Rets: WasmTypeList, - Safety: Safeness, + Inner: Kind, { pub fn params(&self) -> &'static [Type] { Args::types() @@ -122,110 +214,205 @@ where } } +impl WasmTypeList for Infallible { + type CStruct = Infallible; + type RetArray = [u64; 0]; + fn from_ret_array(_: Self::RetArray) -> Self { + unreachable!() + } + fn empty_ret_array() -> Self::RetArray { + unreachable!() + } + fn from_c_struct(_: Self::CStruct) -> Self { + unreachable!() + } + fn into_c_struct(self) -> Self::CStruct { + unreachable!() + } + fn types() -> &'static [Type] { + &[] + } + #[allow(non_snake_case)] + unsafe fn call( + self, + _: NonNull, + _: Wasm, + _: *mut Ctx, + ) -> Result { + unreachable!() + } +} + impl WasmTypeList for (A,) { type CStruct = S1; + type RetArray = [u64; 1]; + fn from_ret_array(array: Self::RetArray) -> Self { + (WasmExternType::from_native(NativeWasmType::from_binary( + array[0], + )),) + } + fn empty_ret_array() -> Self::RetArray { + [0u64] + } fn from_c_struct(c_struct: Self::CStruct) -> Self { let S1(a) = c_struct; - (a,) + (WasmExternType::from_native(a),) } fn into_c_struct(self) -> Self::CStruct { #[allow(unused_parens, non_snake_case)] let (a,) = self; - S1(a) + S1(WasmExternType::to_native(a)) } fn types() -> &'static [Type] { - &[A::TYPE] + &[A::Native::TYPE] } #[allow(non_snake_case)] - unsafe fn call(self, f: *const (), ctx: *mut Ctx) -> Rets { - let f: extern "C" fn(*mut Ctx, A) -> Rets = mem::transmute(f); + unsafe fn call( + self, + f: NonNull, + wasm: Wasm, + ctx: *mut Ctx, + ) -> Result { let (a,) = self; - f(ctx, a) + let args = [a.to_native().to_binary()]; + let mut rets = Rets::empty_ret_array(); + let mut trap = WasmTrapInfo::Unknown; + let mut user_error = None; + + if (wasm.invoke)( + wasm.trampoline, + ctx, + f, + args.as_ptr(), + rets.as_mut().as_mut_ptr(), + &mut trap, + &mut user_error, + wasm.invoke_env, + ) { + Ok(Rets::from_ret_array(rets)) + } else { + if let Some(data) = user_error { + Err(RuntimeError::Error { data }) + } else { + Err(RuntimeError::Trap { + msg: trap.to_string().into(), + }) + } + } } } -impl<'a, A: WasmExternType, Rets> Func<'a, (A,), Rets, Safe> +impl<'a, A: WasmExternType, Rets> Func<'a, (A,), Rets, Wasm> where Rets: WasmTypeList, { pub fn call(&self, a: A) -> Result { - Ok(unsafe { ::call(a, self.f, self.ctx) }) + unsafe { ::call(a, self.f, self.inner, self.ctx) } } } macro_rules! impl_traits { ( [$repr:ident] $struct_name:ident, $( $x:ident ),* ) => { #[repr($repr)] - pub struct $struct_name <$( $x ),*> ( $( $x ),* ); + pub struct $struct_name <$( $x: WasmExternType ),*> ( $( <$x as WasmExternType>::Native ),* ); impl< $( $x: WasmExternType, )* > WasmTypeList for ( $( $x ),* ) { type CStruct = $struct_name<$( $x ),*>; + type RetArray = [u64; count_idents!( $( $x ),* )]; + fn from_ret_array(array: Self::RetArray) -> Self { + #[allow(non_snake_case)] + let [ $( $x ),* ] = array; + ( $( WasmExternType::from_native(NativeWasmType::from_binary($x)) ),* ) + } + fn empty_ret_array() -> Self::RetArray { + [0; count_idents!( $( $x ),* )] + } fn from_c_struct(c_struct: Self::CStruct) -> Self { #[allow(non_snake_case)] let $struct_name ( $( $x ),* ) = c_struct; - ( $( $x ),* ) + ( $( WasmExternType::from_native($x) ),* ) } fn into_c_struct(self) -> Self::CStruct { #[allow(unused_parens, non_snake_case)] let ( $( $x ),* ) = self; - $struct_name ( $( $x ),* ) + $struct_name ( $( WasmExternType::to_native($x) ),* ) } fn types() -> &'static [Type] { - &[$( $x::TYPE, )*] + &[$( $x::Native::TYPE, )*] } #[allow(non_snake_case)] - unsafe fn call(self, f: *const (), ctx: *mut Ctx) -> Rets { - let f: extern fn(*mut Ctx $( ,$x )*) -> Rets::CStruct = mem::transmute(f); + unsafe fn call(self, f: NonNull, wasm: Wasm, ctx: *mut Ctx) -> Result { #[allow(unused_parens)] let ( $( $x ),* ) = self; - let c_struct = f(ctx $( ,$x )*); - Rets::from_c_struct(c_struct) + let args = [ $( $x.to_native().to_binary()),* ]; + let mut rets = Rets::empty_ret_array(); + let mut trap = WasmTrapInfo::Unknown; + let mut user_error = None; + + if (wasm.invoke)(wasm.trampoline, ctx, f, args.as_ptr(), rets.as_mut().as_mut_ptr(), &mut trap, &mut user_error, wasm.invoke_env) { + Ok(Rets::from_ret_array(rets)) + } else { + if let Some(data) = user_error { + Err(RuntimeError::Error { data }) + } else { + Err(RuntimeError::Trap { msg: trap.to_string().into() }) + } + } } } 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 () { + fn to_raw(&self) -> NonNull { 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( &mut Ctx $( ,$x )* ) -> Trap>( ctx: &mut Ctx $( ,$x: $x )* ) -> Rets::CStruct { + /// This is required for the llvm backend to be able to unwind through this function. + #[cfg_attr(nightly, unwind(allowed))] + extern fn wrap<$( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly, FN: Fn( &mut Ctx $( ,$x )* ) -> Trap>( ctx: &mut Ctx $( ,$x: <$x as WasmExternType>::Native )* ) -> Rets::CStruct { let f: FN = unsafe { mem::transmute_copy(&()) }; let err = match panic::catch_unwind(panic::AssertUnwindSafe(|| { - f( ctx $( ,$x )* ).report() + f( ctx $( ,WasmExternType::from_native($x) )* ).report() })) { Ok(Ok(returns)) => return returns.into_c_struct(), - Ok(Err(err)) => err, + Ok(Err(err)) => { + let b: Box<_> = err.into(); + b as Box + }, Err(err) => err, }; unsafe { - if let Some(early_trapper) = &*EARLY_TRAPPER.with(|ucell| ucell.get()) { - early_trapper.do_early_trap(err) - } else { - eprintln!("panic handling not setup"); - std::process::exit(1) - } + (&*ctx.module).runnable_module.do_early_trap(err) } } - wrap::<$( $x, )* Rets, Trap, Self> as *const () + NonNull::new(wrap::<$( $x, )* Rets, Trap, Self> as *mut vm::Func).unwrap() } } - impl<'a, $( $x: WasmExternType, )* Rets> Func<'a, ( $( $x ),* ), Rets, Safe> + impl<'a, $( $x: WasmExternType, )* Rets> Func<'a, ( $( $x ),* ), Rets, Wasm> where Rets: WasmTypeList, { #[allow(non_snake_case)] pub fn call(&self, $( $x: $x, )* ) -> Result { #[allow(unused_parens)] - Ok(unsafe { <( $( $x ),* ) as WasmTypeList>::call(( $($x),* ), self.f, self.ctx) }) + unsafe { <( $( $x ),* ) as WasmTypeList>::call(( $($x),* ), self.f, self.inner, self.ctx) } } } }; } +macro_rules! count_idents { + ( $($idents:ident),* ) => {{ + #[allow(dead_code, non_camel_case_types)] + enum Idents { $($idents,)* __CountIdentsLast } + const COUNT: usize = Idents::__CountIdentsLast as usize; + COUNT + }}; +} + impl_traits!([C] S0,); impl_traits!([transparent] S1, A); impl_traits!([C] S2, A, B); @@ -240,14 +427,14 @@ 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> +impl<'a, Args, Rets, Inner> IsExport for Func<'a, Args, Rets, Inner> where Args: WasmTypeList, Rets: WasmTypeList, - Safety: Safeness, + Inner: Kind, { fn to_export(&self) -> Export { - let func = unsafe { FuncPointer::new(self.f as _) }; + let func = unsafe { FuncPointer::new(self.f.as_ptr()) }; let ctx = Context::Internal; let signature = Arc::new(FuncSig::new(Args::types(), Rets::types())); diff --git a/lib/runtime-core/src/types.rs b/lib/runtime-core/src/types.rs index 9ee5270dd..975b5cfba 100644 --- a/lib/runtime-core/src/types.rs +++ b/lib/runtime-core/src/types.rs @@ -1,5 +1,5 @@ use crate::{memory::MemoryType, module::ModuleInfo, structures::TypedIndex, units::Pages}; -use std::{borrow::Cow, mem}; +use std::borrow::Cow; /// Represents a WebAssembly type. #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -71,29 +71,150 @@ impl From for Value { } } -pub unsafe trait WasmExternType: Copy + Clone +pub unsafe trait NativeWasmType: Copy + Into where Self: Sized, { const TYPE: Type; + fn from_binary(bits: u64) -> Self; + fn to_binary(self) -> u64; +} + +unsafe impl NativeWasmType for i32 { + const TYPE: Type = Type::I32; + fn from_binary(bits: u64) -> Self { + bits as _ + } + fn to_binary(self) -> u64 { + self as _ + } +} +unsafe impl NativeWasmType for i64 { + const TYPE: Type = Type::I64; + fn from_binary(bits: u64) -> Self { + bits as _ + } + fn to_binary(self) -> u64 { + self as _ + } +} +unsafe impl NativeWasmType for f32 { + const TYPE: Type = Type::F32; + fn from_binary(bits: u64) -> Self { + f32::from_bits(bits as u32) + } + fn to_binary(self) -> u64 { + self.to_bits() as _ + } +} +unsafe impl NativeWasmType for f64 { + const TYPE: Type = Type::F64; + fn from_binary(bits: u64) -> Self { + f64::from_bits(bits) + } + fn to_binary(self) -> u64 { + self.to_bits() + } +} + +pub unsafe trait WasmExternType: Copy +where + Self: Sized, +{ + type Native: NativeWasmType; + fn from_native(native: Self::Native) -> Self; + fn to_native(self) -> Self::Native; +} + +unsafe impl WasmExternType for i8 { + type Native = i32; + fn from_native(native: Self::Native) -> Self { + native as _ + } + fn to_native(self) -> Self::Native { + self as _ + } +} +unsafe impl WasmExternType for u8 { + type Native = i32; + fn from_native(native: Self::Native) -> Self { + native as _ + } + fn to_native(self) -> Self::Native { + self as _ + } +} +unsafe impl WasmExternType for i16 { + type Native = i32; + fn from_native(native: Self::Native) -> Self { + native as _ + } + fn to_native(self) -> Self::Native { + self as _ + } +} +unsafe impl WasmExternType for u16 { + type Native = i32; + fn from_native(native: Self::Native) -> Self { + native as _ + } + fn to_native(self) -> Self::Native { + self as _ + } } unsafe impl WasmExternType for i32 { - const TYPE: Type = Type::I32; + type Native = i32; + fn from_native(native: Self::Native) -> Self { + native + } + fn to_native(self) -> Self::Native { + self + } } unsafe impl WasmExternType for u32 { - const TYPE: Type = Type::I32; + type Native = i32; + fn from_native(native: Self::Native) -> Self { + native as _ + } + fn to_native(self) -> Self::Native { + self as _ + } } unsafe impl WasmExternType for i64 { - const TYPE: Type = Type::I64; + type Native = i64; + fn from_native(native: Self::Native) -> Self { + native + } + fn to_native(self) -> Self::Native { + self + } } unsafe impl WasmExternType for u64 { - const TYPE: Type = Type::I64; + type Native = i64; + fn from_native(native: Self::Native) -> Self { + native as _ + } + fn to_native(self) -> Self::Native { + self as _ + } } unsafe impl WasmExternType for f32 { - const TYPE: Type = Type::F32; + type Native = f32; + fn from_native(native: Self::Native) -> Self { + native + } + fn to_native(self) -> Self::Native { + self + } } unsafe impl WasmExternType for f64 { - const TYPE: Type = Type::F64; + type Native = f64; + fn from_native(native: Self::Native) -> Self { + native + } + fn to_native(self) -> Self::Native { + self + } } // pub trait IntegerAtomic @@ -113,34 +234,15 @@ unsafe impl WasmExternType for f64 { // fn swap(&self, other: Self::Primitive) -> Self::Primitive; // } -pub enum ValueError { - BufferTooSmall, -} - -pub trait ValueType: Copy +pub unsafe trait ValueType: Copy where Self: Sized, { - fn into_le(self, buffer: &mut [u8]); - fn from_le(buffer: &[u8]) -> Result; } macro_rules! convert_value_impl { ($t:ty) => { - impl ValueType for $t { - fn into_le(self, buffer: &mut [u8]) { - buffer[..mem::size_of::()].copy_from_slice(&self.to_le_bytes()); - } - fn from_le(buffer: &[u8]) -> Result { - if buffer.len() >= mem::size_of::() { - let mut array = [0u8; mem::size_of::()]; - array.copy_from_slice(&buffer[..mem::size_of::()]); - Ok(Self::from_le_bytes(array)) - } else { - Err(ValueError::BufferTooSmall) - } - } - } + unsafe impl ValueType for $t {} }; ( $($t:ty),* ) => { $( @@ -149,25 +251,7 @@ macro_rules! convert_value_impl { }; } -convert_value_impl!(u8, i8, u16, i16, u32, i32, u64, i64); - -impl ValueType for f32 { - fn into_le(self, buffer: &mut [u8]) { - self.to_bits().into_le(buffer); - } - fn from_le(buffer: &[u8]) -> Result { - Ok(f32::from_bits(::from_le(buffer)?)) - } -} - -impl ValueType for f64 { - fn into_le(self, buffer: &mut [u8]) { - self.to_bits().into_le(buffer); - } - fn from_le(buffer: &[u8]) -> Result { - Ok(f64::from_bits(::from_le(buffer)?)) - } -} +convert_value_impl!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64); #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] pub enum ElementType { @@ -207,6 +291,7 @@ pub enum Initializer { GetGlobal(ImportedGlobalIndex), } +/// Describes the mutability and type of a Global #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] pub struct GlobalDescriptor { pub mutable: bool, @@ -316,7 +401,7 @@ pub trait LocalImport { macro_rules! define_map_index { ($ty:ident) => { #[derive(Serialize, Deserialize)] - #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct $ty (u32); impl TypedIndex for $ty { #[doc(hidden)] @@ -431,3 +516,58 @@ where } } } + +#[cfg(test)] +mod tests { + use crate::types::NativeWasmType; + use crate::types::WasmExternType; + + #[test] + fn test_native_types_round_trip() { + assert_eq!( + 42i32, + i32::from_native(i32::from_binary((42i32).to_native().to_binary())) + ); + + assert_eq!( + -42i32, + i32::from_native(i32::from_binary((-42i32).to_native().to_binary())) + ); + + use std::i64; + let xi64 = i64::MAX; + assert_eq!( + xi64, + i64::from_native(i64::from_binary((xi64).to_native().to_binary())) + ); + let yi64 = i64::MIN; + assert_eq!( + yi64, + i64::from_native(i64::from_binary((yi64).to_native().to_binary())) + ); + + assert_eq!( + 16.5f32, + f32::from_native(f32::from_binary((16.5f32).to_native().to_binary())) + ); + + assert_eq!( + -16.5f32, + f32::from_native(f32::from_binary((-16.5f32).to_native().to_binary())) + ); + + use std::f64; + let xf64: f64 = f64::MAX; + assert_eq!( + xf64, + f64::from_native(f64::from_binary((xf64).to_native().to_binary())) + ); + + let yf64: f64 = f64::MIN; + assert_eq!( + yf64, + f64::from_native(f64::from_binary((yf64).to_native().to_binary())) + ); + } + +} diff --git a/lib/runtime-core/src/units.rs b/lib/runtime-core/src/units.rs index 56d501438..e8232efc8 100644 --- a/lib/runtime-core/src/units.rs +++ b/lib/runtime-core/src/units.rs @@ -4,8 +4,10 @@ use std::{ ops::{Add, Sub}, }; -const WASM_PAGE_SIZE: usize = 65_536; -const WASM_MAX_PAGES: usize = 65_536; +pub const WASM_PAGE_SIZE: usize = 65_536; +pub const WASM_MAX_PAGES: usize = 65_536; +// From emscripten resize_heap implementation +pub const WASM_MIN_PAGES: usize = 256; /// Units of WebAssembly pages (as specified to be 65,536 bytes). #[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] diff --git a/lib/runtime-core/src/vm.rs b/lib/runtime-core/src/vm.rs index b9b6bbe27..a4dac4ce6 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -7,6 +7,8 @@ use crate::{ }; use std::{ffi::c_void, mem, ptr}; +use hashbrown::HashMap; + /// The context of the currently running WebAssembly instance. /// /// This is implicitly passed to every webassembly function. @@ -22,32 +24,8 @@ use std::{ffi::c_void, mem, ptr}; #[derive(Debug)] #[repr(C)] 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, - - /// 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, + // `internal` must be the first field of `Ctx`. + pub(crate) internal: InternalCtx, pub(crate) local_functions: *const *const Func, @@ -55,7 +33,7 @@ pub struct Ctx { /// by the owning `Instance`. local_backing: *mut LocalBacking, import_backing: *mut ImportBacking, - module: *const ModuleInner, + pub module: *const ModuleInner, //// This is intended to be user-supplied, per-instance /// contextual data. There are currently some issue with it, @@ -70,7 +48,42 @@ pub struct Ctx { /// If there's a function set in this field, it gets called /// when the context is destructed, e.g. when an `Instance` /// is dropped. - pub data_finalizer: Option, + pub data_finalizer: Option, +} + +/// The internal context of the currently running WebAssembly instance. +/// +/// +#[doc(hidden)] +#[derive(Debug)] +#[repr(C)] +pub struct InternalCtx { + /// A pointer to an array of locally-defined memories, indexed by `MemoryIndex`. + pub memories: *mut *mut LocalMemory, + + /// A pointer to an array of locally-defined tables, indexed by `TableIndex`. + pub tables: *mut *mut LocalTable, + + /// A pointer to an array of locally-defined globals, indexed by `GlobalIndex`. + pub globals: *mut *mut LocalGlobal, + + /// A pointer to an array of imported memories, indexed by `MemoryIndex, + pub imported_memories: *mut *mut LocalMemory, + + /// A pointer to an array of imported tables, indexed by `TableIndex`. + pub imported_tables: *mut *mut LocalTable, + + /// A pointer to an array of imported globals, indexed by `GlobalIndex`. + pub imported_globals: *mut *mut LocalGlobal, + + /// 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 dynamic_sigindices: *const SigId, } impl Ctx { @@ -81,16 +94,18 @@ impl Ctx { module: &ModuleInner, ) -> Self { Self { - memories: local_backing.vm_memories.as_mut_ptr(), - tables: local_backing.vm_tables.as_mut_ptr(), - globals: local_backing.vm_globals.as_mut_ptr(), + internal: InternalCtx { + memories: local_backing.vm_memories.as_mut_ptr(), + tables: local_backing.vm_tables.as_mut_ptr(), + globals: local_backing.vm_globals.as_mut_ptr(), - imported_memories: import_backing.vm_memories.as_mut_ptr(), - imported_tables: import_backing.vm_tables.as_mut_ptr(), - imported_globals: import_backing.vm_globals.as_mut_ptr(), - imported_funcs: import_backing.vm_functions.as_mut_ptr(), + imported_memories: import_backing.vm_memories.as_mut_ptr(), + imported_tables: import_backing.vm_tables.as_mut_ptr(), + 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(), + dynamic_sigindices: local_backing.dynamic_sigindices.as_ptr(), + }, local_functions: local_backing.local_functions.as_ptr(), local_backing, @@ -108,19 +123,21 @@ impl Ctx { import_backing: &mut ImportBacking, module: &ModuleInner, data: *mut c_void, - data_finalizer: extern "C" fn(*mut c_void), + data_finalizer: fn(*mut c_void), ) -> Self { Self { - memories: local_backing.vm_memories.as_mut_ptr(), - tables: local_backing.vm_tables.as_mut_ptr(), - globals: local_backing.vm_globals.as_mut_ptr(), + internal: InternalCtx { + memories: local_backing.vm_memories.as_mut_ptr(), + tables: local_backing.vm_tables.as_mut_ptr(), + globals: local_backing.vm_globals.as_mut_ptr(), - imported_memories: import_backing.vm_memories.as_mut_ptr(), - imported_tables: import_backing.vm_tables.as_mut_ptr(), - imported_globals: import_backing.vm_globals.as_mut_ptr(), - imported_funcs: import_backing.vm_functions.as_mut_ptr(), + imported_memories: import_backing.vm_memories.as_mut_ptr(), + imported_tables: import_backing.vm_tables.as_mut_ptr(), + 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(), + dynamic_sigindices: local_backing.dynamic_sigindices.as_ptr(), + }, local_functions: local_backing.local_functions.as_ptr(), local_backing, @@ -164,6 +181,11 @@ impl Ctx { }, } } + + /// Gives access to the emscripten symbol map, used for debugging + pub unsafe fn borrow_symbol_map(&self) -> &Option> { + &(*self.module).info.em_symbol_map + } } #[doc(hidden)] @@ -356,43 +378,45 @@ impl Anyfunc { #[cfg(test)] mod vm_offset_tests { - use super::{Anyfunc, Ctx, ImportedFunc, LocalGlobal, LocalMemory, LocalTable}; + use super::{Anyfunc, Ctx, ImportedFunc, InternalCtx, LocalGlobal, LocalMemory, LocalTable}; #[test] fn vmctx() { + assert_eq!(0usize, offset_of!(Ctx => internal).get_byte_offset(),); + assert_eq!( Ctx::offset_memories() as usize, - offset_of!(Ctx => memories).get_byte_offset(), + offset_of!(InternalCtx => memories).get_byte_offset(), ); assert_eq!( Ctx::offset_tables() as usize, - offset_of!(Ctx => tables).get_byte_offset(), + offset_of!(InternalCtx => tables).get_byte_offset(), ); assert_eq!( Ctx::offset_globals() as usize, - offset_of!(Ctx => globals).get_byte_offset(), + offset_of!(InternalCtx => globals).get_byte_offset(), ); assert_eq!( Ctx::offset_imported_memories() as usize, - offset_of!(Ctx => imported_memories).get_byte_offset(), + offset_of!(InternalCtx => imported_memories).get_byte_offset(), ); assert_eq!( Ctx::offset_imported_tables() as usize, - offset_of!(Ctx => imported_tables).get_byte_offset(), + offset_of!(InternalCtx => imported_tables).get_byte_offset(), ); assert_eq!( Ctx::offset_imported_globals() as usize, - offset_of!(Ctx => imported_globals).get_byte_offset(), + offset_of!(InternalCtx => imported_globals).get_byte_offset(), ); assert_eq!( Ctx::offset_imported_funcs() as usize, - offset_of!(Ctx => imported_funcs).get_byte_offset(), + offset_of!(InternalCtx => imported_funcs).get_byte_offset(), ); assert_eq!( @@ -480,7 +504,7 @@ mod vm_ctx_tests { str: String, } - extern "C" fn test_data_finalizer(data: *mut c_void) { + fn test_data_finalizer(data: *mut c_void) { let test_data: &mut TestData = unsafe { &mut *(data as *mut TestData) }; assert_eq!(test_data.x, 10); assert_eq!(test_data.y, true); @@ -543,52 +567,38 @@ mod vm_ctx_tests { fn generate_module() -> ModuleInner { use super::Func; - 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 crate::backend::{sys::Memory, Backend, CacheGen, RunnableModule}; + use crate::cache::Error as CacheError; + use crate::typed_func::Wasm; + use crate::types::{LocalFuncIndex, SigIndex}; use hashbrown::HashMap; + use std::any::Any; use std::ptr::NonNull; struct Placeholder; - impl FuncResolver for Placeholder { - fn get( + impl RunnableModule for Placeholder { + fn get_func( &self, - _module: &ModuleInner, + _module: &ModuleInfo, _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 Ctx, - _: Token, - ) -> RuntimeResult> { - Ok(vec![]) + + fn get_trampoline(&self, _module: &ModuleInfo, _sig_index: SigIndex) -> Option { + unimplemented!() } - fn get_early_trapper(&self) -> Box { + unsafe fn do_early_trap(&self, _: Box) -> ! { unimplemented!() } } impl CacheGen for Placeholder { - fn generate_cache( - &self, - module: &ModuleInner, - ) -> Result<(Box, Box<[u8]>, Memory), CacheError> { + fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError> { unimplemented!() } } ModuleInner { - func_resolver: Box::new(Placeholder), - protected_caller: Box::new(Placeholder), + runnable_module: Box::new(Placeholder), cache_gen: Box::new(Placeholder), info: ModuleInfo { memories: Map::new(), @@ -614,6 +624,10 @@ mod vm_ctx_tests { namespace_table: StringTable::new(), name_table: StringTable::new(), + + em_symbol_map: None, + + custom_sections: HashMap::new(), }, } } diff --git a/lib/runtime-core/src/vmcalls.rs b/lib/runtime-core/src/vmcalls.rs index b428fb24e..4126024bf 100644 --- a/lib/runtime-core/src/vmcalls.rs +++ b/lib/runtime-core/src/vmcalls.rs @@ -17,7 +17,7 @@ pub unsafe extern "C" fn local_static_memory_grow( memory_index: LocalMemoryIndex, delta: Pages, ) -> i32 { - let local_memory = *ctx.memories.add(memory_index.index()); + let local_memory = *ctx.internal.memories.add(memory_index.index()); let memory = (*local_memory).memory as *mut StaticMemory; match (*memory).grow(delta, &mut *local_memory) { @@ -30,7 +30,7 @@ pub unsafe extern "C" fn local_static_memory_size( ctx: &vm::Ctx, memory_index: LocalMemoryIndex, ) -> Pages { - let local_memory = *ctx.memories.add(memory_index.index()); + let local_memory = *ctx.internal.memories.add(memory_index.index()); let memory = (*local_memory).memory as *mut StaticMemory; (*memory).size() @@ -41,7 +41,7 @@ pub unsafe extern "C" fn local_dynamic_memory_grow( memory_index: LocalMemoryIndex, delta: Pages, ) -> i32 { - let local_memory = *ctx.memories.add(memory_index.index()); + let local_memory = *ctx.internal.memories.add(memory_index.index()); let memory = (*local_memory).memory as *mut DynamicMemory; match (*memory).grow(delta, &mut *local_memory) { @@ -54,7 +54,7 @@ pub unsafe extern "C" fn local_dynamic_memory_size( ctx: &vm::Ctx, memory_index: LocalMemoryIndex, ) -> Pages { - let local_memory = *ctx.memories.add(memory_index.index()); + let local_memory = *ctx.internal.memories.add(memory_index.index()); let memory = (*local_memory).memory as *mut DynamicMemory; (*memory).size() @@ -69,7 +69,10 @@ pub unsafe extern "C" fn imported_static_memory_grow( import_memory_index: ImportedMemoryIndex, delta: Pages, ) -> i32 { - let local_memory = *ctx.imported_memories.add(import_memory_index.index()); + let local_memory = *ctx + .internal + .imported_memories + .add(import_memory_index.index()); let memory = (*local_memory).memory as *mut StaticMemory; match (*memory).grow(delta, &mut *local_memory) { @@ -82,7 +85,10 @@ pub unsafe extern "C" fn imported_static_memory_size( ctx: &vm::Ctx, import_memory_index: ImportedMemoryIndex, ) -> Pages { - let local_memory = *ctx.imported_memories.add(import_memory_index.index()); + let local_memory = *ctx + .internal + .imported_memories + .add(import_memory_index.index()); let memory = (*local_memory).memory as *mut StaticMemory; (*memory).size() @@ -93,7 +99,7 @@ pub unsafe extern "C" fn imported_dynamic_memory_grow( memory_index: ImportedMemoryIndex, delta: Pages, ) -> i32 { - let local_memory = *ctx.imported_memories.add(memory_index.index()); + let local_memory = *ctx.internal.imported_memories.add(memory_index.index()); let memory = (*local_memory).memory as *mut DynamicMemory; match (*memory).grow(delta, &mut *local_memory) { @@ -106,7 +112,7 @@ pub unsafe extern "C" fn imported_dynamic_memory_size( ctx: &vm::Ctx, memory_index: ImportedMemoryIndex, ) -> Pages { - let local_memory = *ctx.imported_memories.add(memory_index.index()); + let local_memory = *ctx.internal.imported_memories.add(memory_index.index()); let memory = (*local_memory).memory as *mut DynamicMemory; (*memory).size() diff --git a/lib/runtime/Cargo.toml b/lib/runtime/Cargo.toml index 895423ead..5e620bc66 100644 --- a/lib/runtime/Cargo.toml +++ b/lib/runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime" -version = "0.2.1" +version = "0.4.1" description = "Wasmer runtime library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,16 +9,18 @@ edition = "2018" readme = "README.md" [dependencies] +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.1", optional = true } lazy_static = "1.2.0" memmap = "0.7.0" [dependencies.wasmer-runtime-core] path = "../runtime-core" -version = "0.2.1" +version = "0.4.1" [dependencies.wasmer-clif-backend] path = "../clif-backend" -version = "0.2.0" +version = "0.4.1" +optional = true [dev-dependencies] tempfile = "3.0.7" @@ -27,9 +29,15 @@ wabt = "0.7.4" [target.'cfg(not(windows))'.dependencies.wasmer-llvm-backend] path = "../llvm-backend" +optional = true [features] +default = ["default-compiler"] +default-compiler = ["wasmer-clif-backend"] +cache = ["default-compiler"] debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"] +llvm = ["wasmer-llvm-backend"] +singlepass = ["wasmer-singlepass-backend"] [[bench]] name = "nginx" diff --git a/lib/runtime/README.md b/lib/runtime/README.md index 5e5542c8d..cbb6388aa 100644 --- a/lib/runtime/README.md +++ b/lib/runtime/README.md @@ -1,12 +1,41 @@ -# Wasmer-Runtime +

+ + Wasmer logo + +

-Wasmer-runtime is a library that makes embedding WebAssembly -in your application easy, efficient, and safe. +

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

-# How to use Wasmer-Runtime +# Wasmer Runtime -The easiest way is to use the [`instantiate`] function to create an [`Instance`]. -Then you can use [`call`] or [`func`] and then [`call`][func.call] to call an exported function safely. +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 represents the high-level runtime API, making embedding +WebAssembly in your application easy, efficient, and safe. + +## How to use Wasmer Runtime + +The easiest way is to use the [`instantiate`] function to create an +[`Instance`]. Then you can use [`call`] or [`func`] and then +[`call`][func.call] to call an exported function safely. [`instantiate`]: https://docs.rs/wasmer-runtime/*/wasmer_runtime/fn.instantiate.html [`Instance`]: https://docs.rs/wasmer-runtime/*/wasmer_runtime/struct.Instance.html @@ -14,7 +43,7 @@ Then you can use [`call`] or [`func`] and then [`call`][func.call] to call an ex [`func`]: https://docs.rs/wasmer-runtime/*/wasmer_runtime/struct.Instance.html#method.func [func.call]: https://docs.rs/wasmer-runtime/*/wasmer_runtime/struct.Function.html#method.call -## Here's an example: +## Example Given this WebAssembly: @@ -27,7 +56,7 @@ Given this WebAssembly: i32.add)) ``` -compiled into wasm bytecode, we can call the exported "add_one" function: +compiled into Wasm bytecode, we can call the exported `add_one` function: ```rust static WASM: &'static [u8] = &[ @@ -63,13 +92,14 @@ fn main() -> error::Result<()> { } ``` -# Additional Notes: +## Additional Notes -The `wasmer-runtime` is build to support compiler multiple backends. -Currently, we support the [Cranelift] compiler with the [`wasmer-clif-backend`] crate. +The `wasmer-runtime` crate is build to support multiple compiler +backends. Currently, we support the [Cranelift] compiler with the +[`wasmer-clif-backend`] crate by default. You can specify the compiler you wish to use with the [`compile_with`] function. [Cranelift]: https://github.com/CraneStation/cranelift [`wasmer-clif-backend`]: https://crates.io/crates/wasmer-clif-backend -[`compile_with`]: https://docs.rs/wasmer-runtime/*/wasmer_runtime/fn.compile_with.html \ No newline at end of file +[`compile_with`]: https://docs.rs/wasmer-runtime/*/wasmer_runtime/fn.compile_with.html diff --git a/lib/runtime/examples/call.rs b/lib/runtime/examples/call.rs index dbd29c7e8..cbc632d2e 100644 --- a/lib/runtime/examples/call.rs +++ b/lib/runtime/examples/call.rs @@ -1,11 +1,14 @@ -use wasmer_runtime::{compile, error, imports, Ctx, Func, Value}; +use wasmer_runtime::{compile, error, error::RuntimeError, imports, Ctx, Func, Value}; use wabt::wat2wasm; static WAT: &'static str = r#" (module (type (;0;) (func (result i32))) + (import "env" "do_panic" (func $do_panic (type 0))) (func $dbz (result i32) + call $do_panic + drop i32.const 42 i32.const 0 i32.div_u @@ -29,10 +32,19 @@ fn get_wasm() -> Vec { wat2wasm(WAT).unwrap() } -fn foobar(ctx: &mut Ctx) -> i32 { +fn foobar(_ctx: &mut Ctx) -> i32 { 42 } +#[derive(Debug)] +struct ExitCode { + code: i32, +} + +fn do_panic(_ctx: &mut Ctx) -> Result { + Err(ExitCode { code: 42 }) +} + fn main() -> Result<(), error::Error> { let wasm = get_wasm(); @@ -46,13 +58,23 @@ fn main() -> Result<(), error::Error> { // }; println!("instantiating"); - let instance = module.instantiate(&imports! {})?; + let instance = module.instantiate(&imports! { + "env" => { + "do_panic" => Func::new(do_panic), + }, + })?; - let foo = instance.dyn_func("dbz")?; + let foo: Func<(), i32> = instance.func("dbz")?; - let result = foo.call(&[]); + let result = foo.call(); println!("result: {:?}", result); + if let Err(RuntimeError::Error { data }) = result { + if let Ok(exit_code) = data.downcast::() { + println!("exit code: {:?}", exit_code); + } + } + Ok(()) } diff --git a/lib/runtime/src/cache.rs b/lib/runtime/src/cache.rs index 6ebbf1017..4b2768bc6 100644 --- a/lib/runtime/src/cache.rs +++ b/lib/runtime/src/cache.rs @@ -7,7 +7,7 @@ use std::{ }; use wasmer_runtime_core::cache::Error as CacheError; -pub use wasmer_runtime_core::cache::{Artifact, Cache, WasmHash}; +pub use wasmer_runtime_core::cache::{Artifact, Cache, WasmHash, WASMER_VERSION_HASH}; /// Representation of a directory that contains compiled wasm artifacts. /// @@ -40,12 +40,17 @@ pub struct FileSystemCache { impl FileSystemCache { /// Construct a new `FileSystemCache` around the specified directory. + /// The contents of the cache are stored in sub-versioned directories. /// /// # 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(); + let path: PathBuf = { + let mut path = path.into(); + path.push(WASMER_VERSION_HASH); + path + }; if path.exists() { let metadata = path.metadata()?; diff --git a/lib/runtime/src/lib.rs b/lib/runtime/src/lib.rs index 4ad74c345..bfdf6c9f7 100644 --- a/lib/runtime/src/lib.rs +++ b/lib/runtime/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + //! Wasmer-runtime is a library that makes embedding WebAssembly //! in your application easy, efficient, and safe. //! @@ -74,6 +76,7 @@ //! [`wasmer-clif-backend`]: https://crates.io/crates/wasmer-clif-backend //! [`compile_with`]: fn.compile_with.html +pub use wasmer_runtime_core::export::Export; pub use wasmer_runtime_core::global::Global; pub use wasmer_runtime_core::import::ImportObject; pub use wasmer_runtime_core::instance::{DynFunc, Instance}; @@ -95,7 +98,9 @@ pub mod wasm { //! Various types exposed by the Wasmer Runtime. pub use wasmer_runtime_core::global::Global; pub use wasmer_runtime_core::table::Table; - pub use wasmer_runtime_core::types::{FuncSig, MemoryDescriptor, TableDescriptor, Type, Value}; + pub use wasmer_runtime_core::types::{ + FuncSig, GlobalDescriptor, MemoryDescriptor, TableDescriptor, Type, Value, + }; } pub mod error { @@ -110,7 +115,7 @@ pub mod units { pub mod cache; -use wasmer_runtime_core::backend::Compiler; +use wasmer_runtime_core::backend::{Compiler, CompilerConfig}; /// Compile WebAssembly binary code into a [`Module`]. /// This function is useful if it is necessary to @@ -129,6 +134,25 @@ pub fn compile(wasm: &[u8]) -> error::CompileResult { wasmer_runtime_core::compile_with(&wasm[..], default_compiler()) } +/// The same as `compile` but takes a `CompilerConfig` for the purpose of +/// changing the compiler's behavior +pub fn compile_with_config( + wasm: &[u8], + compiler_config: CompilerConfig, +) -> error::CompileResult { + wasmer_runtime_core::compile_with_config(&wasm[..], default_compiler(), compiler_config) +} + +/// The same as `compile_with_config` but takes a `Compiler` for the purpose of +/// changing the backend. +pub fn compile_with_config_with( + wasm: &[u8], + compiler_config: CompilerConfig, + compiler: &dyn Compiler, +) -> error::CompileResult { + wasmer_runtime_core::compile_with_config(&wasm[..], compiler, compiler_config) +} + /// Compile and instantiate WebAssembly code without /// creating a [`Module`]. /// @@ -152,13 +176,17 @@ pub fn instantiate(wasm: &[u8], import_object: &ImportObject) -> error::Result &'static dyn Compiler { +/// Get a single instance of the default compiler to use. +pub fn default_compiler() -> &'static dyn Compiler { use lazy_static::lazy_static; #[cfg(feature = "llvm")] use wasmer_llvm_backend::LLVMCompiler as DefaultCompiler; - #[cfg(not(feature = "llvm"))] + #[cfg(feature = "singlepass")] + use wasmer_singlepass_backend::SinglePassCompiler as DefaultCompiler; + + #[cfg(not(any(feature = "llvm", feature = "singlepass")))] use wasmer_clif_backend::CraneliftCompiler as DefaultCompiler; lazy_static! { diff --git a/lib/runtime/tests/error_propagation.rs b/lib/runtime/tests/error_propagation.rs new file mode 100644 index 000000000..1b9f9fccf --- /dev/null +++ b/lib/runtime/tests/error_propagation.rs @@ -0,0 +1,49 @@ +#[test] +fn error_propagation() { + use std::convert::Infallible; + use wabt::wat2wasm; + use wasmer_runtime::{compile, error::RuntimeError, imports, Ctx, Func}; + + static WAT: &'static str = r#" + (module + (type (;0;) (func)) + (import "env" "ret_err" (func $ret_err (type 0))) + (func $call_panic + call $ret_err + ) + (export "call_err" (func $call_panic)) + ) + "#; + + #[derive(Debug)] + struct ExitCode { + code: i32, + } + + fn ret_err(_ctx: &mut Ctx) -> Result { + Err(ExitCode { code: 42 }) + } + + let wasm = wat2wasm(WAT).unwrap(); + + let module = compile(&wasm).unwrap(); + + let instance = module + .instantiate(&imports! { + "env" => { + "ret_err" => Func::new(ret_err), + }, + }) + .unwrap(); + + let foo: Func<(), ()> = instance.func("call_err").unwrap(); + + let result = foo.call(); + + if let Err(RuntimeError::Error { data }) = result { + let exit_code = data.downcast::().unwrap(); + assert_eq!(exit_code.code, 42); + } else { + panic!("didn't return RuntimeError::Error") + } +} diff --git a/lib/singlepass-backend/Cargo.toml b/lib/singlepass-backend/Cargo.toml new file mode 100644 index 000000000..72ec5200a --- /dev/null +++ b/lib/singlepass-backend/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "wasmer-singlepass-backend" +version = "0.4.1" +repository = "https://github.com/wasmerio/wasmer" +description = "Wasmer runtime single pass compiler backend" +license = "MIT" +authors = ["The Wasmer Engineering Team "] +edition = "2018" + +[dependencies] +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } +wasmparser = "0.29.2" +dynasm = "0.3.1" +dynasmrt = "0.3.1" +lazy_static = "1.2.0" +byteorder = "1" +nix = "0.13.0" +libc = "0.2.49" +smallvec = "0.6.9" +hashbrown = "0.1" diff --git a/lib/singlepass-backend/README.md b/lib/singlepass-backend/README.md new file mode 100644 index 000000000..3d8c63615 --- /dev/null +++ b/lib/singlepass-backend/README.md @@ -0,0 +1,31 @@ +

+ + Wasmer logo + +

+ +

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

+ +# Wasmer singlepass backend + +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 represents the singlepass backend. diff --git a/lib/singlepass-backend/src/codegen_x64.rs b/lib/singlepass-backend/src/codegen_x64.rs new file mode 100644 index 000000000..9aa6c9151 --- /dev/null +++ b/lib/singlepass-backend/src/codegen_x64.rs @@ -0,0 +1,4214 @@ +#![allow(clippy::forget_copy)] // Used by dynasm. + +use crate::emitter_x64::*; +use crate::machine::*; +use crate::protect_unix; +use dynasmrt::{ + x64::Assembler, AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi, ExecutableBuffer, +}; +use smallvec::SmallVec; +use std::ptr::NonNull; +use std::{any::Any, collections::HashMap, sync::Arc}; +use wasmer_runtime_core::{ + backend::{sys::Memory, Backend, CacheGen, RunnableModule, Token}, + cache::{Artifact, Error as CacheError}, + codegen::*, + memory::MemoryType, + module::{ModuleInfo, ModuleInner}, + structures::{Map, TypedIndex}, + typed_func::Wasm, + types::{ + FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex, + TableIndex, Type, + }, + vm::{self, LocalGlobal, LocalMemory, LocalTable}, + vmcalls, +}; +use wasmparser::{Operator, Type as WpType}; + +lazy_static! { + /// Performs a System V call to `target` with [stack_top..stack_base] as the argument list, from right to left. + static ref CONSTRUCT_STACK_AND_CALL_WASM: unsafe extern "C" fn (stack_top: *const u64, stack_base: *const u64, ctx: *mut vm::Ctx, target: *const vm::Func) -> u64 = { + let mut assembler = Assembler::new().unwrap(); + let offset = assembler.offset(); + dynasm!( + assembler + ; push r15 + ; push r14 + ; push r13 + ; push r12 + ; push r11 + ; push rbp + ; mov rbp, rsp + + ; mov r15, rdi + ; mov r14, rsi + ; mov r13, rdx + ; mov r12, rcx + + ; mov rdi, r13 // ctx + + ; sub r14, 8 + ; cmp r14, r15 + ; jb >stack_ready + + ; mov rsi, [r14] + ; sub r14, 8 + ; cmp r14, r15 + ; jb >stack_ready + + ; mov rdx, [r14] + ; sub r14, 8 + ; cmp r14, r15 + ; jb >stack_ready + + ; mov rcx, [r14] + ; sub r14, 8 + ; cmp r14, r15 + ; jb >stack_ready + + ; mov r8, [r14] + ; sub r14, 8 + ; cmp r14, r15 + ; jb >stack_ready + + ; mov r9, [r14] + ; sub r14, 8 + ; cmp r14, r15 + ; jb >stack_ready + + ; mov rax, r14 + ; sub rax, r15 + ; sub rsp, rax + ; sub rsp, 8 + ; mov rax, QWORD 0xfffffffffffffff0u64 as i64 + ; and rsp, rax + ; mov rax, rsp + ; loop_begin: + ; mov r11, [r14] + ; mov [rax], r11 + ; sub r14, 8 + ; add rax, 8 + ; cmp r14, r15 + ; jb >stack_ready + ; jmp , + signatures: Option>>, + function_signatures: Option>>, + function_labels: Option)>>, + assembler: Option, + func_import_count: usize, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +enum LocalOrTemp { + Local, + Temp, +} + +pub struct X64FunctionCode { + signatures: Arc>, + function_signatures: Arc>, + + assembler: Option, + function_labels: Option)>>, + br_table_data: Option>>, + breakpoints: Option>>, + returns: SmallVec<[WpType; 1]>, + locals: Vec, + num_params: usize, + num_locals: usize, + value_stack: Vec<(Location, LocalOrTemp)>, + control_stack: Vec, + machine: Machine, + unreachable_depth: usize, +} + +enum FuncPtrInner {} +#[repr(transparent)] +#[derive(Copy, Clone, Debug)] +struct FuncPtr(*const FuncPtrInner); +unsafe impl Send for FuncPtr {} +unsafe impl Sync for FuncPtr {} + +pub struct X64ExecutionContext { + #[allow(dead_code)] + code: ExecutableBuffer, + #[allow(dead_code)] + functions: Vec, + function_pointers: Vec, + signatures: Arc>, + _br_table_data: Vec>, + breakpoints: Arc>>, + func_import_count: usize, +} + +#[derive(Debug)] +pub struct ControlFrame { + pub label: DynamicLabel, + pub loop_like: bool, + pub if_else: IfElseState, + pub returns: SmallVec<[WpType; 1]>, + pub value_stack_depth: usize, +} + +#[derive(Debug, Copy, Clone)] +pub enum IfElseState { + None, + If(DynamicLabel), + Else, +} + +impl RunnableModule for X64ExecutionContext { + fn get_func( + &self, + _: &ModuleInfo, + local_func_index: LocalFuncIndex, + ) -> Option> { + self.function_pointers[self.func_import_count..] + .get(local_func_index.index()) + .and_then(|ptr| NonNull::new(ptr.0 as *mut vm::Func)) + } + + fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option { + use std::ffi::c_void; + use wasmer_runtime_core::typed_func::WasmTrapInfo; + + unsafe extern "C" fn invoke( + _trampoline: unsafe extern "C" fn( + *mut vm::Ctx, + NonNull, + *const u64, + *mut u64, + ), + ctx: *mut vm::Ctx, + func: NonNull, + args: *const u64, + rets: *mut u64, + trap_info: *mut WasmTrapInfo, + user_error: *mut Option>, + num_params_plus_one: Option>, + ) -> bool { + let rm: &Box = &(&*(*ctx).module).runnable_module; + let execution_context = + ::std::mem::transmute_copy::<&dyn RunnableModule, &X64ExecutionContext>(&&**rm); + + let args = ::std::slice::from_raw_parts( + args, + num_params_plus_one.unwrap().as_ptr() as usize - 1, + ); + let args_reverse: SmallVec<[u64; 8]> = args.iter().cloned().rev().collect(); + protect_unix::BKPT_MAP + .with(|x| x.borrow_mut().push(execution_context.breakpoints.clone())); + let ret = match protect_unix::call_protected(|| { + CONSTRUCT_STACK_AND_CALL_WASM( + args_reverse.as_ptr(), + args_reverse.as_ptr().offset(args_reverse.len() as isize), + ctx, + func.as_ptr(), + ) + }) { + Ok(x) => { + if !rets.is_null() { + *rets = x; + } + true + } + Err(err) => { + match err { + protect_unix::CallProtError::Trap(info) => *trap_info = info, + protect_unix::CallProtError::Error(data) => *user_error = Some(data), + } + false + } + }; + protect_unix::BKPT_MAP.with(|x| x.borrow_mut().pop().unwrap()); + ret + } + + unsafe extern "C" fn dummy_trampoline( + _: *mut vm::Ctx, + _: NonNull, + _: *const u64, + _: *mut u64, + ) { + unreachable!() + } + + Some(unsafe { + Wasm::from_raw_parts( + dummy_trampoline, + invoke, + NonNull::new((self.signatures.get(sig_index).unwrap().params().len() + 1) as _), // +1 to keep it non-zero + ) + }) + } + + unsafe fn do_early_trap(&self, data: Box) -> ! { + protect_unix::TRAP_EARLY_DATA.with(|x| x.set(Some(data))); + protect_unix::trigger_trap(); + } +} + +#[derive(Debug)] +pub struct CodegenError { + pub message: &'static str, +} + +impl ModuleCodeGenerator + for X64ModuleCodeGenerator +{ + fn new() -> X64ModuleCodeGenerator { + X64ModuleCodeGenerator { + functions: vec![], + signatures: None, + function_signatures: None, + function_labels: Some(HashMap::new()), + assembler: Some(Assembler::new().unwrap()), + func_import_count: 0, + } + } + + fn backend_id() -> Backend { + Backend::Singlepass + } + + fn check_precondition(&mut self, _module_info: &ModuleInfo) -> Result<(), CodegenError> { + Ok(()) + } + + fn next_function(&mut self) -> Result<&mut X64FunctionCode, CodegenError> { + let (mut assembler, mut function_labels, br_table_data, breakpoints) = + match self.functions.last_mut() { + Some(x) => ( + x.assembler.take().unwrap(), + x.function_labels.take().unwrap(), + x.br_table_data.take().unwrap(), + x.breakpoints.take().unwrap(), + ), + None => ( + self.assembler.take().unwrap(), + self.function_labels.take().unwrap(), + vec![], + HashMap::new(), + ), + }; + let begin_offset = assembler.offset(); + let begin_label_info = function_labels + .entry(self.functions.len() + self.func_import_count) + .or_insert_with(|| (assembler.new_dynamic_label(), None)); + + begin_label_info.1 = Some(begin_offset); + let begin_label = begin_label_info.0; + + dynasm!( + assembler + ; => begin_label + //; int 3 + ); + let code = X64FunctionCode { + signatures: self.signatures.as_ref().unwrap().clone(), + function_signatures: self.function_signatures.as_ref().unwrap().clone(), + + assembler: Some(assembler), + function_labels: Some(function_labels), + br_table_data: Some(br_table_data), + breakpoints: Some(breakpoints), + returns: smallvec![], + locals: vec![], + num_params: 0, + num_locals: 0, + value_stack: vec![], + control_stack: vec![], + machine: Machine::new(), + unreachable_depth: 0, + }; + self.functions.push(code); + Ok(self.functions.last_mut().unwrap()) + } + + fn finalize( + mut self, + _: &ModuleInfo, + ) -> Result<(X64ExecutionContext, Box), CodegenError> { + let (assembler, mut br_table_data, breakpoints) = match self.functions.last_mut() { + Some(x) => ( + x.assembler.take().unwrap(), + x.br_table_data.take().unwrap(), + x.breakpoints.take().unwrap(), + ), + None => { + return Err(CodegenError { + message: "no function", + }); + } + }; + let output = assembler.finalize().unwrap(); + + for table in &mut br_table_data { + for entry in table { + *entry = output.ptr(AssemblyOffset(*entry)) as usize; + } + } + + let function_labels = if let Some(x) = self.functions.last() { + x.function_labels.as_ref().unwrap() + } else { + self.function_labels.as_ref().unwrap() + }; + let mut out_labels: Vec = vec![]; + + for i in 0..function_labels.len() { + let (_, offset) = match function_labels.get(&i) { + Some(x) => x, + None => { + return Err(CodegenError { + message: "label not found", + }); + } + }; + let offset = match offset { + Some(x) => x, + None => { + return Err(CodegenError { + message: "offset is none", + }); + } + }; + out_labels.push(FuncPtr(output.ptr(*offset) as _)); + } + + let breakpoints: Arc> = Arc::new( + breakpoints + .into_iter() + .map(|(offset, f)| (output.ptr(offset) as usize, f)) + .collect(), + ); + + struct Placeholder; + impl CacheGen for Placeholder { + fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError> { + Err(CacheError::Unknown( + "the singlepass backend doesn't support caching yet".to_string(), + )) + } + } + + Ok(( + X64ExecutionContext { + code: output, + functions: self.functions, + signatures: self.signatures.as_ref().unwrap().clone(), + _br_table_data: br_table_data, + breakpoints: breakpoints, + func_import_count: self.func_import_count, + function_pointers: out_labels, + }, + Box::new(Placeholder), + )) + } + + fn feed_signatures(&mut self, signatures: Map) -> Result<(), CodegenError> { + self.signatures = Some(Arc::new(signatures)); + Ok(()) + } + + fn feed_function_signatures( + &mut self, + assoc: Map, + ) -> Result<(), CodegenError> { + self.function_signatures = Some(Arc::new(assoc)); + Ok(()) + } + + fn feed_import_function(&mut self) -> Result<(), CodegenError> { + let labels = self.function_labels.as_mut().unwrap(); + let id = labels.len(); + + let a = self.assembler.as_mut().unwrap(); + let offset = a.offset(); + let label = a.get_label(); + a.emit_label(label); + labels.insert(id, (label, Some(offset))); + + // Emits a tail call trampoline that loads the address of the target import function + // from Ctx and jumps to it. + + a.emit_mov( + Size::S64, + Location::Memory(GPR::RDI, vm::Ctx::offset_imported_funcs() as i32), + Location::GPR(GPR::RAX), + ); + a.emit_mov( + Size::S64, + Location::Memory( + GPR::RAX, + (vm::ImportedFunc::size() as usize * id + vm::ImportedFunc::offset_func() as usize) + as i32, + ), + Location::GPR(GPR::RAX), + ); + a.emit_jmp_location(Location::GPR(GPR::RAX)); + + self.func_import_count += 1; + + Ok(()) + } + + unsafe fn from_cache(_artifact: Artifact, _: Token) -> Result { + Err(CacheError::Unknown( + "the singlepass compiler API doesn't support caching yet".to_string(), + )) + } +} + +impl X64FunctionCode { + /// Moves `loc` to a valid location for `div`/`idiv`. + fn emit_relaxed_xdiv( + a: &mut Assembler, + _m: &mut Machine, + op: fn(&mut Assembler, Size, Location), + sz: Size, + loc: Location, + ) { + match loc { + Location::Imm64(_) | Location::Imm32(_) => { + a.emit_mov(sz, loc, Location::GPR(GPR::RCX)); // must not be used during div (rax, rdx) + op(a, sz, Location::GPR(GPR::RCX)); + } + _ => { + op(a, sz, loc); + } + } + } + + /// Moves `src` and `dst` to valid locations for `movzx`/`movsx`. + fn emit_relaxed_zx_sx( + a: &mut Assembler, + m: &mut Machine, + op: fn(&mut Assembler, Size, Location, Size, Location), + sz_src: Size, + mut src: Location, + sz_dst: Size, + dst: Location, + ) { + let tmp_src = m.acquire_temp_gpr().unwrap(); + let tmp_dst = m.acquire_temp_gpr().unwrap(); + + match src { + Location::Imm32(_) | Location::Imm64(_) => { + a.emit_mov(Size::S64, src, Location::GPR(tmp_src)); + src = Location::GPR(tmp_src); + } + Location::Memory(_, _) | Location::GPR(_) => {} + _ => unreachable!(), + } + + match dst { + Location::Imm32(_) | Location::Imm64(_) => unreachable!(), + Location::Memory(_, _) => { + op(a, sz_src, src, sz_dst, Location::GPR(tmp_dst)); + a.emit_mov(Size::S64, Location::GPR(tmp_dst), dst); + } + Location::GPR(_) => { + op(a, sz_src, src, sz_dst, dst); + } + _ => unreachable!(), + } + + m.release_temp_gpr(tmp_dst); + m.release_temp_gpr(tmp_src); + } + + /// Moves `src` and `dst` to valid locations for generic instructions. + fn emit_relaxed_binop( + a: &mut Assembler, + m: &mut Machine, + op: fn(&mut Assembler, Size, Location, Location), + sz: Size, + src: Location, + dst: Location, + ) { + enum RelaxMode { + Direct, + SrcToGPR, + DstToGPR, + BothToGPR, + } + let mode = match (src, dst) { + (Location::Memory(_, _), Location::Memory(_, _)) => RelaxMode::SrcToGPR, + (Location::Imm64(_), Location::Imm64(_)) | (Location::Imm64(_), Location::Imm32(_)) => { + RelaxMode::BothToGPR + } + (_, Location::Imm32(_)) | (_, Location::Imm64(_)) => RelaxMode::DstToGPR, + (Location::Imm64(_), Location::Memory(_, _)) => RelaxMode::SrcToGPR, + (Location::Imm64(_), Location::GPR(_)) + if (op as *const u8 != Assembler::emit_mov as *const u8) => + { + RelaxMode::SrcToGPR + } + (_, Location::XMM(_)) => RelaxMode::SrcToGPR, + _ if (op as *const u8 == Assembler::emit_imul as *const u8) => RelaxMode::BothToGPR, // TODO: optimize this + _ => RelaxMode::Direct, + }; + + match mode { + RelaxMode::SrcToGPR => { + let temp = m.acquire_temp_gpr().unwrap(); + a.emit_mov(sz, src, Location::GPR(temp)); + op(a, sz, Location::GPR(temp), dst); + m.release_temp_gpr(temp); + } + RelaxMode::DstToGPR => { + let temp = m.acquire_temp_gpr().unwrap(); + a.emit_mov(sz, dst, Location::GPR(temp)); + op(a, sz, src, Location::GPR(temp)); + m.release_temp_gpr(temp); + } + RelaxMode::BothToGPR => { + let temp_src = m.acquire_temp_gpr().unwrap(); + let temp_dst = m.acquire_temp_gpr().unwrap(); + a.emit_mov(sz, src, Location::GPR(temp_src)); + a.emit_mov(sz, dst, Location::GPR(temp_dst)); + op(a, sz, Location::GPR(temp_src), Location::GPR(temp_dst)); + match dst { + Location::Memory(_, _) | Location::GPR(_) => { + a.emit_mov(sz, Location::GPR(temp_dst), dst); + } + _ => {} + } + m.release_temp_gpr(temp_dst); + m.release_temp_gpr(temp_src); + } + RelaxMode::Direct => { + op(a, sz, src, dst); + } + } + } + + /// Moves `src1` and `src2` to valid locations and possibly adds a layer of indirection for `dst` for AVX instructions. + fn emit_relaxed_avx( + a: &mut Assembler, + m: &mut Machine, + op: fn(&mut Assembler, XMM, XMMOrMemory, XMM), + src1: Location, + src2: Location, + dst: Location, + ) { + Self::emit_relaxed_avx_base( + a, + m, + |a, _, src1, src2, dst| op(a, src1, src2, dst), + src1, + src2, + dst, + ) + } + + /// Moves `src1` and `src2` to valid locations and possibly adds a layer of indirection for `dst` for AVX instructions. + fn emit_relaxed_avx_base( + a: &mut Assembler, + m: &mut Machine, + op: F, + src1: Location, + src2: Location, + dst: Location, + ) { + let tmp1 = m.acquire_temp_xmm().unwrap(); + let tmp2 = m.acquire_temp_xmm().unwrap(); + let tmp3 = m.acquire_temp_xmm().unwrap(); + let tmpg = m.acquire_temp_gpr().unwrap(); + + let src1 = match src1 { + Location::XMM(x) => x, + Location::GPR(_) | Location::Memory(_, _) => { + a.emit_mov(Size::S64, src1, Location::XMM(tmp1)); + tmp1 + } + Location::Imm32(_) => { + a.emit_mov(Size::S32, src1, Location::GPR(tmpg)); + a.emit_mov(Size::S32, Location::GPR(tmpg), Location::XMM(tmp1)); + tmp1 + } + Location::Imm64(_) => { + a.emit_mov(Size::S64, src1, Location::GPR(tmpg)); + a.emit_mov(Size::S64, Location::GPR(tmpg), Location::XMM(tmp1)); + tmp1 + } + _ => unreachable!(), + }; + + let src2 = match src2 { + Location::XMM(x) => XMMOrMemory::XMM(x), + Location::Memory(base, disp) => XMMOrMemory::Memory(base, disp), + Location::GPR(_) => { + a.emit_mov(Size::S64, src2, Location::XMM(tmp2)); + XMMOrMemory::XMM(tmp2) + } + Location::Imm32(_) => { + a.emit_mov(Size::S32, src2, Location::GPR(tmpg)); + a.emit_mov(Size::S32, Location::GPR(tmpg), Location::XMM(tmp2)); + XMMOrMemory::XMM(tmp2) + } + Location::Imm64(_) => { + a.emit_mov(Size::S64, src2, Location::GPR(tmpg)); + a.emit_mov(Size::S64, Location::GPR(tmpg), Location::XMM(tmp2)); + XMMOrMemory::XMM(tmp2) + } + _ => unreachable!(), + }; + + match dst { + Location::XMM(x) => { + op(a, m, src1, src2, x); + } + Location::Memory(_, _) | Location::GPR(_) => { + op(a, m, src1, src2, tmp3); + a.emit_mov(Size::S64, Location::XMM(tmp3), dst); + } + _ => unreachable!(), + } + + m.release_temp_gpr(tmpg); + m.release_temp_xmm(tmp3); + m.release_temp_xmm(tmp2); + m.release_temp_xmm(tmp1); + } + + /// I32 binary operation with both operands popped from the virtual stack. + fn emit_binop_i32( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + f: fn(&mut Assembler, Size, Location, Location), + ) { + // Using Red Zone here. + let loc_b = get_location_released(a, m, value_stack.pop().unwrap()); + let loc_a = get_location_released(a, m, value_stack.pop().unwrap()); + let ret = m.acquire_locations(a, &[WpType::I32], false)[0]; + + if loc_a != ret { + let tmp = m.acquire_temp_gpr().unwrap(); + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S32, + loc_a, + Location::GPR(tmp), + ); + Self::emit_relaxed_binop(a, m, f, Size::S32, loc_b, Location::GPR(tmp)); + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S32, + Location::GPR(tmp), + ret, + ); + m.release_temp_gpr(tmp); + } else { + Self::emit_relaxed_binop(a, m, f, Size::S32, loc_b, ret); + } + + value_stack.push((ret, LocalOrTemp::Temp)); + } + + /// I64 binary operation with both operands popped from the virtual stack. + fn emit_binop_i64( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + f: fn(&mut Assembler, Size, Location, Location), + ) { + // Using Red Zone here. + let loc_b = get_location_released(a, m, value_stack.pop().unwrap()); + let loc_a = get_location_released(a, m, value_stack.pop().unwrap()); + let ret = m.acquire_locations(a, &[WpType::I64], false)[0]; + + if loc_a != ret { + let tmp = m.acquire_temp_gpr().unwrap(); + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S64, + loc_a, + Location::GPR(tmp), + ); + Self::emit_relaxed_binop(a, m, f, Size::S64, loc_b, Location::GPR(tmp)); + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S64, + Location::GPR(tmp), + ret, + ); + m.release_temp_gpr(tmp); + } else { + Self::emit_relaxed_binop(a, m, f, Size::S64, loc_b, ret); + } + + value_stack.push((ret, LocalOrTemp::Temp)); + } + + /// I32 comparison with `loc_b` from input. + fn emit_cmpop_i32_dynamic_b( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + c: Condition, + loc_b: Location, + ) { + // Using Red Zone here. + let loc_a = get_location_released(a, m, value_stack.pop().unwrap()); + + let ret = m.acquire_locations(a, &[WpType::I32], false)[0]; + match ret { + Location::GPR(x) => { + Self::emit_relaxed_binop(a, m, Assembler::emit_cmp, Size::S32, loc_b, loc_a); + a.emit_set(c, x); + a.emit_and(Size::S32, Location::Imm32(0xff), Location::GPR(x)); + } + Location::Memory(_, _) => { + let tmp = m.acquire_temp_gpr().unwrap(); + Self::emit_relaxed_binop(a, m, Assembler::emit_cmp, Size::S32, loc_b, loc_a); + a.emit_set(c, tmp); + a.emit_and(Size::S32, Location::Imm32(0xff), Location::GPR(tmp)); + a.emit_mov(Size::S32, Location::GPR(tmp), ret); + m.release_temp_gpr(tmp); + } + _ => unreachable!(), + } + value_stack.push((ret, LocalOrTemp::Temp)); + } + + /// I32 comparison with both operands popped from the virtual stack. + fn emit_cmpop_i32( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + c: Condition, + ) { + let loc_b = get_location_released(a, m, value_stack.pop().unwrap()); + Self::emit_cmpop_i32_dynamic_b(a, m, value_stack, c, loc_b); + } + + /// I64 comparison with `loc_b` from input. + fn emit_cmpop_i64_dynamic_b( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + c: Condition, + loc_b: Location, + ) { + // Using Red Zone here. + let loc_a = get_location_released(a, m, value_stack.pop().unwrap()); + + let ret = m.acquire_locations(a, &[WpType::I32], false)[0]; + match ret { + Location::GPR(x) => { + Self::emit_relaxed_binop(a, m, Assembler::emit_cmp, Size::S64, loc_b, loc_a); + a.emit_set(c, x); + a.emit_and(Size::S32, Location::Imm32(0xff), Location::GPR(x)); + } + Location::Memory(_, _) => { + let tmp = m.acquire_temp_gpr().unwrap(); + Self::emit_relaxed_binop(a, m, Assembler::emit_cmp, Size::S64, loc_b, loc_a); + a.emit_set(c, tmp); + a.emit_and(Size::S32, Location::Imm32(0xff), Location::GPR(tmp)); + a.emit_mov(Size::S32, Location::GPR(tmp), ret); + m.release_temp_gpr(tmp); + } + _ => unreachable!(), + } + value_stack.push((ret, LocalOrTemp::Temp)); + } + + /// I64 comparison with both operands popped from the virtual stack. + fn emit_cmpop_i64( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + c: Condition, + ) { + let loc_b = get_location_released(a, m, value_stack.pop().unwrap()); + Self::emit_cmpop_i64_dynamic_b(a, m, value_stack, c, loc_b); + } + + /// I32 `lzcnt`/`tzcnt`/`popcnt` with operand popped from the virtual stack. + fn emit_xcnt_i32( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + f: fn(&mut Assembler, Size, Location, Location), + ) { + let loc = get_location_released(a, m, value_stack.pop().unwrap()); + let ret = m.acquire_locations(a, &[WpType::I32], false)[0]; + + match loc { + Location::Imm32(_) => { + let tmp = m.acquire_temp_gpr().unwrap(); + a.emit_mov(Size::S32, loc, Location::GPR(tmp)); + if let Location::Memory(_, _) = ret { + let out_tmp = m.acquire_temp_gpr().unwrap(); + f(a, Size::S32, Location::GPR(tmp), Location::GPR(out_tmp)); + a.emit_mov(Size::S32, Location::GPR(out_tmp), ret); + m.release_temp_gpr(out_tmp); + } else { + f(a, Size::S32, Location::GPR(tmp), ret); + } + m.release_temp_gpr(tmp); + } + Location::Memory(_, _) | Location::GPR(_) => { + if let Location::Memory(_, _) = ret { + let out_tmp = m.acquire_temp_gpr().unwrap(); + f(a, Size::S32, loc, Location::GPR(out_tmp)); + a.emit_mov(Size::S32, Location::GPR(out_tmp), ret); + m.release_temp_gpr(out_tmp); + } else { + f(a, Size::S32, loc, ret); + } + } + _ => unreachable!(), + } + value_stack.push((ret, LocalOrTemp::Temp)); + } + + /// I64 `lzcnt`/`tzcnt`/`popcnt` with operand popped from the virtual stack. + fn emit_xcnt_i64( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + f: fn(&mut Assembler, Size, Location, Location), + ) { + let loc = get_location_released(a, m, value_stack.pop().unwrap()); + let ret = m.acquire_locations(a, &[WpType::I64], false)[0]; + + match loc { + Location::Imm64(_) | Location::Imm32(_) => { + let tmp = m.acquire_temp_gpr().unwrap(); + a.emit_mov(Size::S64, loc, Location::GPR(tmp)); + if let Location::Memory(_, _) = ret { + let out_tmp = m.acquire_temp_gpr().unwrap(); + f(a, Size::S64, Location::GPR(tmp), Location::GPR(out_tmp)); + a.emit_mov(Size::S64, Location::GPR(out_tmp), ret); + m.release_temp_gpr(out_tmp); + } else { + f(a, Size::S64, Location::GPR(tmp), ret); + } + m.release_temp_gpr(tmp); + } + Location::Memory(_, _) | Location::GPR(_) => { + if let Location::Memory(_, _) = ret { + let out_tmp = m.acquire_temp_gpr().unwrap(); + f(a, Size::S64, loc, Location::GPR(out_tmp)); + a.emit_mov(Size::S64, Location::GPR(out_tmp), ret); + m.release_temp_gpr(out_tmp); + } else { + f(a, Size::S64, loc, ret); + } + } + _ => unreachable!(), + } + value_stack.push((ret, LocalOrTemp::Temp)); + } + + /// I32 shift with both operands popped from the virtual stack. + fn emit_shift_i32( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + f: fn(&mut Assembler, Size, Location, Location), + ) { + let loc_b = get_location_released(a, m, value_stack.pop().unwrap()); + let loc_a = get_location_released(a, m, value_stack.pop().unwrap()); + let ret = m.acquire_locations(a, &[WpType::I32], false)[0]; + + a.emit_mov(Size::S32, loc_b, Location::GPR(GPR::RCX)); + + if loc_a != ret { + Self::emit_relaxed_binop(a, m, Assembler::emit_mov, Size::S32, loc_a, ret); + } + + f(a, Size::S32, Location::GPR(GPR::RCX), ret); + value_stack.push((ret, LocalOrTemp::Temp)); + } + + /// I64 shift with both operands popped from the virtual stack. + fn emit_shift_i64( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + f: fn(&mut Assembler, Size, Location, Location), + ) { + let loc_b = get_location_released(a, m, value_stack.pop().unwrap()); + let loc_a = get_location_released(a, m, value_stack.pop().unwrap()); + let ret = m.acquire_locations(a, &[WpType::I64], false)[0]; + + a.emit_mov(Size::S64, loc_b, Location::GPR(GPR::RCX)); + + if loc_a != ret { + Self::emit_relaxed_binop(a, m, Assembler::emit_mov, Size::S64, loc_a, ret); + } + + f(a, Size::S64, Location::GPR(GPR::RCX), ret); + value_stack.push((ret, LocalOrTemp::Temp)); + } + + /// Floating point (AVX) binary operation with both operands popped from the virtual stack. + fn emit_fp_binop_avx( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + f: fn(&mut Assembler, XMM, XMMOrMemory, XMM), + ) { + let loc_b = get_location_released(a, m, value_stack.pop().unwrap()); + let loc_a = get_location_released(a, m, value_stack.pop().unwrap()); + let ret = m.acquire_locations(a, &[WpType::F64], false)[0]; + value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_relaxed_avx(a, m, f, loc_a, loc_b, ret); + } + + /// Floating point (AVX) comparison with both operands popped from the virtual stack. + fn emit_fp_cmpop_avx( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + f: fn(&mut Assembler, XMM, XMMOrMemory, XMM), + ) { + let loc_b = get_location_released(a, m, value_stack.pop().unwrap()); + let loc_a = get_location_released(a, m, value_stack.pop().unwrap()); + let ret = m.acquire_locations(a, &[WpType::I32], false)[0]; + value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_relaxed_avx(a, m, f, loc_a, loc_b, ret); + a.emit_and(Size::S32, Location::Imm32(1), ret); // FIXME: Why? + } + + /// Floating point (AVX) binary operation with both operands popped from the virtual stack. + fn emit_fp_unop_avx( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + f: fn(&mut Assembler, XMM, XMMOrMemory, XMM), + ) { + let loc = get_location_released(a, m, value_stack.pop().unwrap()); + let ret = m.acquire_locations(a, &[WpType::F64], false)[0]; + value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_relaxed_avx(a, m, f, loc, loc, ret); + } + + /// Emits a System V call sequence. + /// + /// This function must not use RAX before `cb` is called. + fn emit_call_sysv, F: FnOnce(&mut Assembler)>( + a: &mut Assembler, + m: &mut Machine, + cb: F, + params: I, + ) { + let params: Vec<_> = params.collect(); + + // Save used GPRs. + let used_gprs = m.get_used_gprs(); + for r in used_gprs.iter() { + a.emit_push(Size::S64, Location::GPR(*r)); + } + + // Save used XMM registers. + let used_xmms = m.get_used_xmms(); + if used_xmms.len() > 0 { + a.emit_sub( + Size::S64, + Location::Imm32((used_xmms.len() * 8) as u32), + Location::GPR(GPR::RSP), + ); + + // FIXME: Possible dynasm bug. This is a workaround. + // Using RSP as the source/destination operand of a `mov` instruction produces invalid code. + a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RCX)); + for (i, r) in used_xmms.iter().enumerate() { + a.emit_mov( + Size::S64, + Location::XMM(*r), + Location::Memory(GPR::RCX, (i * 8) as i32), + ); + } + } + + let mut stack_offset: usize = 0; + + // Calculate stack offset. + for (i, _param) in params.iter().enumerate() { + let loc = Machine::get_param_location(1 + i); + match loc { + Location::Memory(_, _) => { + stack_offset += 8; + } + _ => {} + } + } + + // Align stack to 16 bytes. + if (m.get_stack_offset() + used_gprs.len() * 8 + stack_offset) % 16 != 0 { + a.emit_sub(Size::S64, Location::Imm32(8), Location::GPR(GPR::RSP)); + stack_offset += 8; + } + + let mut call_movs: Vec<(Location, GPR)> = vec![]; + + // Prepare register & stack parameters. + for (i, param) in params.iter().enumerate().rev() { + let loc = Machine::get_param_location(1 + i); + match loc { + Location::GPR(x) => { + call_movs.push((*param, x)); + } + Location::Memory(_, _) => { + match *param { + // Dynasm bug: RSP in memory operand does not work + Location::Imm64(_) | Location::XMM(_) => { + a.emit_mov( + Size::S64, + Location::GPR(GPR::RAX), + Location::XMM(XMM::XMM0), + ); + a.emit_mov( + Size::S64, + Location::GPR(GPR::RCX), + Location::XMM(XMM::XMM1), + ); + a.emit_sub(Size::S64, Location::Imm32(8), Location::GPR(GPR::RSP)); + a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RCX)); + a.emit_mov(Size::S64, *param, Location::GPR(GPR::RAX)); + a.emit_mov( + Size::S64, + Location::GPR(GPR::RAX), + Location::Memory(GPR::RCX, 0), + ); + a.emit_mov( + Size::S64, + Location::XMM(XMM::XMM0), + Location::GPR(GPR::RAX), + ); + a.emit_mov( + Size::S64, + Location::XMM(XMM::XMM1), + Location::GPR(GPR::RCX), + ); + } + _ => a.emit_push(Size::S64, *param), + } + } + _ => unreachable!(), + } + } + + // Sort register moves so that register are not overwritten before read. + sort_call_movs(&mut call_movs); + + // Emit register moves. + for (loc, gpr) in call_movs { + if loc != Location::GPR(gpr) { + a.emit_mov(Size::S64, loc, Location::GPR(gpr)); + } + } + + // Put vmctx as the first parameter. + a.emit_mov( + Size::S64, + Location::GPR(Machine::get_vmctx_reg()), + Machine::get_param_location(0), + ); // vmctx + + cb(a); + + // Restore stack. + if stack_offset > 0 { + a.emit_add( + Size::S64, + Location::Imm32(stack_offset as u32), + Location::GPR(GPR::RSP), + ); + } + + // Restore XMMs. + if used_xmms.len() > 0 { + // FIXME: Possible dynasm bug. This is a workaround. + a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RDX)); + for (i, r) in used_xmms.iter().enumerate() { + a.emit_mov( + Size::S64, + Location::Memory(GPR::RDX, (i * 8) as i32), + Location::XMM(*r), + ); + } + a.emit_add( + Size::S64, + Location::Imm32((used_xmms.len() * 8) as u32), + Location::GPR(GPR::RSP), + ); + } + + // Restore GPRs. + for r in used_gprs.iter().rev() { + a.emit_pop(Size::S64, Location::GPR(*r)); + } + } + + /// Emits a System V call sequence, specialized for labels as the call target. + fn emit_call_sysv_label>( + a: &mut Assembler, + m: &mut Machine, + label: DynamicLabel, + params: I, + ) { + Self::emit_call_sysv(a, m, |a| a.emit_call_label(label), params) + } + + /// Emits a memory operation. + fn emit_memory_op( + module_info: &ModuleInfo, + a: &mut Assembler, + m: &mut Machine, + addr: Location, + offset: usize, + value_size: usize, + cb: F, + ) { + let tmp_addr = m.acquire_temp_gpr().unwrap(); + let tmp_base = m.acquire_temp_gpr().unwrap(); + let tmp_bound = m.acquire_temp_gpr().unwrap(); + + // Loads both base and bound into temporary registers. + a.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + match MemoryIndex::new(0).local_or_import(module_info) { + LocalOrImport::Local(_) => vm::Ctx::offset_memories(), + LocalOrImport::Import(_) => vm::Ctx::offset_imported_memories(), + } as i32, + ), + Location::GPR(tmp_base), + ); + a.emit_mov( + Size::S64, + Location::Memory(tmp_base, 0), + Location::GPR(tmp_base), + ); + a.emit_mov( + Size::S32, + Location::Memory(tmp_base, LocalMemory::offset_bound() as i32), + Location::GPR(tmp_bound), + ); + a.emit_mov( + Size::S64, + Location::Memory(tmp_base, LocalMemory::offset_base() as i32), + Location::GPR(tmp_base), + ); + + // Adds base to bound so `tmp_bound` now holds the end of linear memory. + a.emit_add(Size::S64, Location::GPR(tmp_base), Location::GPR(tmp_bound)); + + // If the memory is dynamic, we need to do bound checking at runtime. + let mem_desc = match MemoryIndex::new(0).local_or_import(module_info) { + LocalOrImport::Local(local_mem_index) => &module_info.memories[local_mem_index], + LocalOrImport::Import(import_mem_index) => { + &module_info.imported_memories[import_mem_index].1 + } + }; + let need_check = match mem_desc.memory_type() { + MemoryType::Dynamic => true, + MemoryType::Static | MemoryType::SharedStatic => false, + }; + + if need_check { + a.emit_mov(Size::S32, addr, Location::GPR(tmp_addr)); + + // This branch is used for emitting "faster" code for the special case of (offset + value_size) not exceeding u32 range. + match (offset as u32).checked_add(value_size as u32) { + Some(x) => { + a.emit_add(Size::S64, Location::Imm32(x), Location::GPR(tmp_addr)); + } + None => { + a.emit_add( + Size::S64, + Location::Imm32(offset as u32), + Location::GPR(tmp_addr), + ); + a.emit_add( + Size::S64, + Location::Imm32(value_size as u32), + Location::GPR(tmp_addr), + ); + } + } + + // Trap if the end address of the requested area is above that of the linear memory. + a.emit_add(Size::S64, Location::GPR(tmp_base), Location::GPR(tmp_addr)); + a.emit_cmp(Size::S64, Location::GPR(tmp_bound), Location::GPR(tmp_addr)); + a.emit_conditional_trap(Condition::Above); + } + + m.release_temp_gpr(tmp_bound); + + // Calculates the real address, and loads from it. + a.emit_mov(Size::S32, addr, Location::GPR(tmp_addr)); + a.emit_add( + Size::S64, + Location::Imm32(offset as u32), + Location::GPR(tmp_addr), + ); + a.emit_add(Size::S64, Location::GPR(tmp_base), Location::GPR(tmp_addr)); + m.release_temp_gpr(tmp_base); + + cb(a, m, tmp_addr); + + m.release_temp_gpr(tmp_addr); + } + + // Checks for underflow/overflow/nan before IxxTrunc{U/S}F32. + fn emit_f32_int_conv_check( + a: &mut Assembler, + m: &mut Machine, + reg: XMM, + lower_bound: f32, + upper_bound: f32, + ) { + let lower_bound = f32::to_bits(lower_bound); + let upper_bound = f32::to_bits(upper_bound); + + let trap = a.get_label(); + let end = a.get_label(); + + let tmp = m.acquire_temp_gpr().unwrap(); + let tmp_x = m.acquire_temp_xmm().unwrap(); + + // Underflow. + a.emit_mov(Size::S32, Location::Imm32(lower_bound), Location::GPR(tmp)); + a.emit_mov(Size::S32, Location::GPR(tmp), Location::XMM(tmp_x)); + a.emit_vcmpless(reg, XMMOrMemory::XMM(tmp_x), tmp_x); + a.emit_mov(Size::S32, Location::XMM(tmp_x), Location::GPR(tmp)); + a.emit_cmp(Size::S32, Location::Imm32(0), Location::GPR(tmp)); + a.emit_jmp(Condition::NotEqual, trap); + + // Overflow. + a.emit_mov(Size::S32, Location::Imm32(upper_bound), Location::GPR(tmp)); + a.emit_mov(Size::S32, Location::GPR(tmp), Location::XMM(tmp_x)); + a.emit_vcmpgess(reg, XMMOrMemory::XMM(tmp_x), tmp_x); + a.emit_mov(Size::S32, Location::XMM(tmp_x), Location::GPR(tmp)); + a.emit_cmp(Size::S32, Location::Imm32(0), Location::GPR(tmp)); + a.emit_jmp(Condition::NotEqual, trap); + + // NaN. + a.emit_vcmpeqss(reg, XMMOrMemory::XMM(reg), tmp_x); + a.emit_mov(Size::S32, Location::XMM(tmp_x), Location::GPR(tmp)); + a.emit_cmp(Size::S32, Location::Imm32(0), Location::GPR(tmp)); + a.emit_jmp(Condition::Equal, trap); + + a.emit_jmp(Condition::None, end); + a.emit_label(trap); + a.emit_ud2(); + a.emit_label(end); + + m.release_temp_xmm(tmp_x); + m.release_temp_gpr(tmp); + } + + // Checks for underflow/overflow/nan before IxxTrunc{U/S}F64. + fn emit_f64_int_conv_check( + a: &mut Assembler, + m: &mut Machine, + reg: XMM, + lower_bound: f64, + upper_bound: f64, + ) { + let lower_bound = f64::to_bits(lower_bound); + let upper_bound = f64::to_bits(upper_bound); + + let trap = a.get_label(); + let end = a.get_label(); + + let tmp = m.acquire_temp_gpr().unwrap(); + let tmp_x = m.acquire_temp_xmm().unwrap(); + + // Underflow. + a.emit_mov(Size::S64, Location::Imm64(lower_bound), Location::GPR(tmp)); + a.emit_mov(Size::S64, Location::GPR(tmp), Location::XMM(tmp_x)); + a.emit_vcmplesd(reg, XMMOrMemory::XMM(tmp_x), tmp_x); + a.emit_mov(Size::S32, Location::XMM(tmp_x), Location::GPR(tmp)); + a.emit_cmp(Size::S32, Location::Imm32(0), Location::GPR(tmp)); + a.emit_jmp(Condition::NotEqual, trap); + + // Overflow. + a.emit_mov(Size::S64, Location::Imm64(upper_bound), Location::GPR(tmp)); + a.emit_mov(Size::S64, Location::GPR(tmp), Location::XMM(tmp_x)); + a.emit_vcmpgesd(reg, XMMOrMemory::XMM(tmp_x), tmp_x); + a.emit_mov(Size::S32, Location::XMM(tmp_x), Location::GPR(tmp)); + a.emit_cmp(Size::S32, Location::Imm32(0), Location::GPR(tmp)); + a.emit_jmp(Condition::NotEqual, trap); + + // NaN. + a.emit_vcmpeqsd(reg, XMMOrMemory::XMM(reg), tmp_x); + a.emit_mov(Size::S32, Location::XMM(tmp_x), Location::GPR(tmp)); + a.emit_cmp(Size::S32, Location::Imm32(0), Location::GPR(tmp)); + a.emit_jmp(Condition::Equal, trap); + + a.emit_jmp(Condition::None, end); + a.emit_label(trap); + a.emit_ud2(); + a.emit_label(end); + + m.release_temp_xmm(tmp_x); + m.release_temp_gpr(tmp); + } +} + +impl FunctionCodeGenerator for X64FunctionCode { + fn feed_return(&mut self, ty: WpType) -> Result<(), CodegenError> { + self.returns.push(ty); + Ok(()) + } + + fn feed_param(&mut self, _ty: WpType) -> Result<(), CodegenError> { + self.num_params += 1; + self.num_locals += 1; + Ok(()) + } + + fn feed_local(&mut self, _ty: WpType, n: usize) -> Result<(), CodegenError> { + self.num_locals += n; + Ok(()) + } + + fn begin_body(&mut self, _module_info: &ModuleInfo) -> Result<(), CodegenError> { + let a = self.assembler.as_mut().unwrap(); + a.emit_push(Size::S64, Location::GPR(GPR::RBP)); + a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RBP)); + + self.locals = self + .machine + .init_locals(a, self.num_locals, self.num_params); + + a.emit_sub(Size::S64, Location::Imm32(32), Location::GPR(GPR::RSP)); // simulate "red zone" if not supported by the platform + + self.control_stack.push(ControlFrame { + label: a.get_label(), + loop_like: false, + if_else: IfElseState::None, + returns: self.returns.clone(), + value_stack_depth: 0, + }); + Ok(()) + } + + fn finalize(&mut self) -> Result<(), CodegenError> { + let a = self.assembler.as_mut().unwrap(); + a.emit_ud2(); + Ok(()) + } + + fn feed_event(&mut self, ev: Event, module_info: &ModuleInfo) -> Result<(), CodegenError> { + //println!("{:?} {}", op, self.value_stack.len()); + let was_unreachable; + + if self.unreachable_depth > 0 { + was_unreachable = true; + + if let Event::Wasm(op) = ev { + match *op { + Operator::Block { .. } | Operator::Loop { .. } | Operator::If { .. } => { + self.unreachable_depth += 1; + } + Operator::End => { + self.unreachable_depth -= 1; + } + Operator::Else => { + // We are in a reachable true branch + if self.unreachable_depth == 1 { + if let Some(IfElseState::If(_)) = + self.control_stack.last().map(|x| x.if_else) + { + self.unreachable_depth -= 1; + } + } + } + _ => {} + } + } + if self.unreachable_depth > 0 { + return Ok(()); + } + } else { + was_unreachable = false; + } + + let a = self.assembler.as_mut().unwrap(); + + let op = match ev { + Event::Wasm(x) => x, + Event::Internal(x) => { + match x { + InternalEvent::Breakpoint(callback) => { + a.emit_bkpt(); + self.breakpoints + .as_mut() + .unwrap() + .insert(a.get_offset(), callback); + } + InternalEvent::FunctionBegin(_) | InternalEvent::FunctionEnd => {} + _ => unimplemented!(), + } + return Ok(()); + } + }; + match *op { + Operator::GetGlobal { global_index } => { + let global_index = global_index as usize; + + let tmp = self.machine.acquire_temp_gpr().unwrap(); + + let loc = match GlobalIndex::new(global_index).local_or_import(module_info) { + LocalOrImport::Local(local_index) => { + a.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + vm::Ctx::offset_globals() as i32, + ), + Location::GPR(tmp), + ); + a.emit_mov( + Size::S64, + Location::Memory(tmp, (local_index.index() as i32) * 8), + Location::GPR(tmp), + ); + self.machine.acquire_locations( + a, + &[type_to_wp_type(module_info.globals[local_index].desc.ty)], + false, + )[0] + } + LocalOrImport::Import(import_index) => { + a.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + vm::Ctx::offset_imported_globals() as i32, + ), + Location::GPR(tmp), + ); + a.emit_mov( + Size::S64, + Location::Memory(tmp, (import_index.index() as i32) * 8), + Location::GPR(tmp), + ); + self.machine.acquire_locations( + a, + &[type_to_wp_type( + module_info.imported_globals[import_index].1.ty, + )], + false, + )[0] + } + }; + self.value_stack.push((loc, LocalOrTemp::Temp)); + + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + Location::Memory(tmp, LocalGlobal::offset_data() as i32), + loc, + ); + + self.machine.release_temp_gpr(tmp); + } + Operator::SetGlobal { global_index } => { + let mut global_index = global_index as usize; + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + let tmp = self.machine.acquire_temp_gpr().unwrap(); + + if global_index < module_info.imported_globals.len() { + a.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + vm::Ctx::offset_imported_globals() as i32, + ), + Location::GPR(tmp), + ); + } else { + global_index -= module_info.imported_globals.len(); + assert!(global_index < module_info.globals.len()); + a.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + vm::Ctx::offset_globals() as i32, + ), + Location::GPR(tmp), + ); + } + a.emit_mov( + Size::S64, + Location::Memory(tmp, (global_index as i32) * 8), + Location::GPR(tmp), + ); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + Location::Memory(tmp, LocalGlobal::offset_data() as i32), + ); + + self.machine.release_temp_gpr(tmp); + } + Operator::GetLocal { local_index } => { + let local_index = local_index as usize; + self.value_stack + .push((self.locals[local_index], LocalOrTemp::Local)); + } + Operator::SetLocal { local_index } => { + let local_index = local_index as usize; + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + self.locals[local_index], + ); + } + Operator::TeeLocal { local_index } => { + let local_index = local_index as usize; + let (loc, _) = *self.value_stack.last().unwrap(); + + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + self.locals[local_index], + ); + } + Operator::I32Const { value } => self + .value_stack + .push((Location::Imm32(value as u32), LocalOrTemp::Temp)), + Operator::I32Add => Self::emit_binop_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_add, + ), + Operator::I32Sub => Self::emit_binop_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_sub, + ), + Operator::I32Mul => Self::emit_binop_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_imul, + ), + Operator::I32DivU => { + // We assume that RAX and RDX are temporary registers here. + let loc_b = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let loc_a = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + a.emit_mov(Size::S32, loc_a, Location::GPR(GPR::RAX)); + a.emit_xor(Size::S32, Location::GPR(GPR::RDX), Location::GPR(GPR::RDX)); + Self::emit_relaxed_xdiv( + a, + &mut self.machine, + Assembler::emit_div, + Size::S32, + loc_b, + ); + a.emit_mov(Size::S32, Location::GPR(GPR::RAX), ret); + self.value_stack.push((ret, LocalOrTemp::Temp)); + } + Operator::I32DivS => { + // We assume that RAX and RDX are temporary registers here. + let loc_b = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let loc_a = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + a.emit_mov(Size::S32, loc_a, Location::GPR(GPR::RAX)); + a.emit_cdq(); + Self::emit_relaxed_xdiv( + a, + &mut self.machine, + Assembler::emit_idiv, + Size::S32, + loc_b, + ); + a.emit_mov(Size::S32, Location::GPR(GPR::RAX), ret); + self.value_stack.push((ret, LocalOrTemp::Temp)); + } + Operator::I32RemU => { + // We assume that RAX and RDX are temporary registers here. + let loc_b = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let loc_a = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + a.emit_mov(Size::S32, loc_a, Location::GPR(GPR::RAX)); + a.emit_xor(Size::S32, Location::GPR(GPR::RDX), Location::GPR(GPR::RDX)); + Self::emit_relaxed_xdiv( + a, + &mut self.machine, + Assembler::emit_div, + Size::S32, + loc_b, + ); + a.emit_mov(Size::S32, Location::GPR(GPR::RDX), ret); + self.value_stack.push((ret, LocalOrTemp::Temp)); + } + Operator::I32RemS => { + // We assume that RAX and RDX are temporary registers here. + let loc_b = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let loc_a = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + + let normal_path = a.get_label(); + let end = a.get_label(); + + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_cmp, + Size::S32, + Location::Imm32(0x80000000), + loc_a, + ); + a.emit_jmp(Condition::NotEqual, normal_path); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_cmp, + Size::S32, + Location::Imm32(0xffffffff), + loc_b, + ); + a.emit_jmp(Condition::NotEqual, normal_path); + a.emit_mov(Size::S32, Location::Imm32(0), ret); + a.emit_jmp(Condition::None, end); + + a.emit_label(normal_path); + a.emit_mov(Size::S32, loc_a, Location::GPR(GPR::RAX)); + a.emit_cdq(); + Self::emit_relaxed_xdiv( + a, + &mut self.machine, + Assembler::emit_idiv, + Size::S32, + loc_b, + ); + a.emit_mov(Size::S32, Location::GPR(GPR::RDX), ret); + self.value_stack.push((ret, LocalOrTemp::Temp)); + + a.emit_label(end); + } + Operator::I32And => Self::emit_binop_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_and, + ), + Operator::I32Or => Self::emit_binop_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_or, + ), + Operator::I32Xor => Self::emit_binop_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_xor, + ), + Operator::I32Eq => Self::emit_cmpop_i32( + a, + &mut self.machine, + &mut self.value_stack, + Condition::Equal, + ), + Operator::I32Ne => Self::emit_cmpop_i32( + a, + &mut self.machine, + &mut self.value_stack, + Condition::NotEqual, + ), + Operator::I32Eqz => Self::emit_cmpop_i32_dynamic_b( + a, + &mut self.machine, + &mut self.value_stack, + Condition::Equal, + Location::Imm32(0), + ), + Operator::I32Clz => Self::emit_xcnt_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_lzcnt, + ), + Operator::I32Ctz => Self::emit_xcnt_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_tzcnt, + ), + Operator::I32Popcnt => Self::emit_xcnt_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_popcnt, + ), + Operator::I32Shl => Self::emit_shift_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_shl, + ), + Operator::I32ShrU => Self::emit_shift_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_shr, + ), + Operator::I32ShrS => Self::emit_shift_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_sar, + ), + Operator::I32Rotl => Self::emit_shift_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_rol, + ), + Operator::I32Rotr => Self::emit_shift_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_ror, + ), + Operator::I32LtU => Self::emit_cmpop_i32( + a, + &mut self.machine, + &mut self.value_stack, + Condition::Below, + ), + Operator::I32LeU => Self::emit_cmpop_i32( + a, + &mut self.machine, + &mut self.value_stack, + Condition::BelowEqual, + ), + Operator::I32GtU => Self::emit_cmpop_i32( + a, + &mut self.machine, + &mut self.value_stack, + Condition::Above, + ), + Operator::I32GeU => Self::emit_cmpop_i32( + a, + &mut self.machine, + &mut self.value_stack, + Condition::AboveEqual, + ), + Operator::I32LtS => { + Self::emit_cmpop_i32(a, &mut self.machine, &mut self.value_stack, Condition::Less) + } + Operator::I32LeS => Self::emit_cmpop_i32( + a, + &mut self.machine, + &mut self.value_stack, + Condition::LessEqual, + ), + Operator::I32GtS => Self::emit_cmpop_i32( + a, + &mut self.machine, + &mut self.value_stack, + Condition::Greater, + ), + Operator::I32GeS => Self::emit_cmpop_i32( + a, + &mut self.machine, + &mut self.value_stack, + Condition::GreaterEqual, + ), + Operator::I64Const { value } => { + let value = value as u64; + self.value_stack + .push((Location::Imm64(value), LocalOrTemp::Temp)); + } + Operator::I64Add => Self::emit_binop_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_add, + ), + Operator::I64Sub => Self::emit_binop_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_sub, + ), + Operator::I64Mul => Self::emit_binop_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_imul, + ), + Operator::I64DivU => { + // We assume that RAX and RDX are temporary registers here. + let loc_b = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let loc_a = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + a.emit_mov(Size::S64, loc_a, Location::GPR(GPR::RAX)); + a.emit_xor(Size::S64, Location::GPR(GPR::RDX), Location::GPR(GPR::RDX)); + Self::emit_relaxed_xdiv( + a, + &mut self.machine, + Assembler::emit_div, + Size::S64, + loc_b, + ); + a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + self.value_stack.push((ret, LocalOrTemp::Temp)); + } + Operator::I64DivS => { + // We assume that RAX and RDX are temporary registers here. + let loc_b = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let loc_a = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + a.emit_mov(Size::S64, loc_a, Location::GPR(GPR::RAX)); + a.emit_cqo(); + Self::emit_relaxed_xdiv( + a, + &mut self.machine, + Assembler::emit_idiv, + Size::S64, + loc_b, + ); + a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + self.value_stack.push((ret, LocalOrTemp::Temp)); + } + Operator::I64RemU => { + // We assume that RAX and RDX are temporary registers here. + let loc_b = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let loc_a = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + a.emit_mov(Size::S64, loc_a, Location::GPR(GPR::RAX)); + a.emit_xor(Size::S64, Location::GPR(GPR::RDX), Location::GPR(GPR::RDX)); + Self::emit_relaxed_xdiv( + a, + &mut self.machine, + Assembler::emit_div, + Size::S64, + loc_b, + ); + a.emit_mov(Size::S64, Location::GPR(GPR::RDX), ret); + self.value_stack.push((ret, LocalOrTemp::Temp)); + } + Operator::I64RemS => { + // We assume that RAX and RDX are temporary registers here. + let loc_b = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let loc_a = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + + let normal_path = a.get_label(); + let end = a.get_label(); + + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_cmp, + Size::S64, + Location::Imm64(0x8000000000000000u64), + loc_a, + ); + a.emit_jmp(Condition::NotEqual, normal_path); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_cmp, + Size::S64, + Location::Imm64(0xffffffffffffffffu64), + loc_b, + ); + a.emit_jmp(Condition::NotEqual, normal_path); + a.emit_mov(Size::S64, Location::Imm64(0), ret); + a.emit_jmp(Condition::None, end); + + a.emit_label(normal_path); + + a.emit_mov(Size::S64, loc_a, Location::GPR(GPR::RAX)); + a.emit_cqo(); + Self::emit_relaxed_xdiv( + a, + &mut self.machine, + Assembler::emit_idiv, + Size::S64, + loc_b, + ); + a.emit_mov(Size::S64, Location::GPR(GPR::RDX), ret); + self.value_stack.push((ret, LocalOrTemp::Temp)); + a.emit_label(end); + } + Operator::I64And => Self::emit_binop_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_and, + ), + Operator::I64Or => Self::emit_binop_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_or, + ), + Operator::I64Xor => Self::emit_binop_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_xor, + ), + Operator::I64Eq => Self::emit_cmpop_i64( + a, + &mut self.machine, + &mut self.value_stack, + Condition::Equal, + ), + Operator::I64Ne => Self::emit_cmpop_i64( + a, + &mut self.machine, + &mut self.value_stack, + Condition::NotEqual, + ), + Operator::I64Eqz => Self::emit_cmpop_i64_dynamic_b( + a, + &mut self.machine, + &mut self.value_stack, + Condition::Equal, + Location::Imm64(0), + ), + Operator::I64Clz => Self::emit_xcnt_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_lzcnt, + ), + Operator::I64Ctz => Self::emit_xcnt_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_tzcnt, + ), + Operator::I64Popcnt => Self::emit_xcnt_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_popcnt, + ), + Operator::I64Shl => Self::emit_shift_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_shl, + ), + Operator::I64ShrU => Self::emit_shift_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_shr, + ), + Operator::I64ShrS => Self::emit_shift_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_sar, + ), + Operator::I64Rotl => Self::emit_shift_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_rol, + ), + Operator::I64Rotr => Self::emit_shift_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_ror, + ), + Operator::I64LtU => Self::emit_cmpop_i64( + a, + &mut self.machine, + &mut self.value_stack, + Condition::Below, + ), + Operator::I64LeU => Self::emit_cmpop_i64( + a, + &mut self.machine, + &mut self.value_stack, + Condition::BelowEqual, + ), + Operator::I64GtU => Self::emit_cmpop_i64( + a, + &mut self.machine, + &mut self.value_stack, + Condition::Above, + ), + Operator::I64GeU => Self::emit_cmpop_i64( + a, + &mut self.machine, + &mut self.value_stack, + Condition::AboveEqual, + ), + Operator::I64LtS => { + Self::emit_cmpop_i64(a, &mut self.machine, &mut self.value_stack, Condition::Less) + } + Operator::I64LeS => Self::emit_cmpop_i64( + a, + &mut self.machine, + &mut self.value_stack, + Condition::LessEqual, + ), + Operator::I64GtS => Self::emit_cmpop_i64( + a, + &mut self.machine, + &mut self.value_stack, + Condition::Greater, + ), + Operator::I64GeS => Self::emit_cmpop_i64( + a, + &mut self.machine, + &mut self.value_stack, + Condition::GreaterEqual, + ), + Operator::I64ExtendUI32 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + loc, + ret, + ); + } + Operator::I64ExtendSI32 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + Self::emit_relaxed_zx_sx( + a, + &mut self.machine, + Assembler::emit_movsx, + Size::S32, + loc, + Size::S64, + ret, + ); + } + Operator::I32WrapI64 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + loc, + ret, + ); + } + + Operator::F32Const { value } => self + .value_stack + .push((Location::Imm32(value.bits()), LocalOrTemp::Temp)), + Operator::F32Add => Self::emit_fp_binop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vaddss, + ), + Operator::F32Sub => Self::emit_fp_binop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vsubss, + ), + Operator::F32Mul => Self::emit_fp_binop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vmulss, + ), + Operator::F32Div => Self::emit_fp_binop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vdivss, + ), + Operator::F32Max => Self::emit_fp_binop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vmaxss, + ), + Operator::F32Min => Self::emit_fp_binop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vminss, + ), + Operator::F32Eq => Self::emit_fp_cmpop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vcmpeqss, + ), + Operator::F32Ne => Self::emit_fp_cmpop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vcmpneqss, + ), + Operator::F32Lt => Self::emit_fp_cmpop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vcmpltss, + ), + Operator::F32Le => Self::emit_fp_cmpop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vcmpless, + ), + Operator::F32Gt => Self::emit_fp_cmpop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vcmpgtss, + ), + Operator::F32Ge => Self::emit_fp_cmpop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vcmpgess, + ), + Operator::F32Nearest => Self::emit_fp_unop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vroundss_nearest, + ), + Operator::F32Floor => Self::emit_fp_unop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vroundss_floor, + ), + Operator::F32Ceil => Self::emit_fp_unop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vroundss_ceil, + ), + Operator::F32Trunc => Self::emit_fp_unop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vroundss_trunc, + ), + Operator::F32Sqrt => Self::emit_fp_unop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vsqrtss, + ), + + Operator::F32Copysign => { + let loc_b = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let loc_a = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + let tmp1 = self.machine.acquire_temp_gpr().unwrap(); + let tmp2 = self.machine.acquire_temp_gpr().unwrap(); + a.emit_mov(Size::S32, loc_a, Location::GPR(tmp1)); + a.emit_mov(Size::S32, loc_b, Location::GPR(tmp2)); + a.emit_and( + Size::S32, + Location::Imm32(0x7fffffffu32), + Location::GPR(tmp1), + ); + a.emit_and( + Size::S32, + Location::Imm32(0x80000000u32), + Location::GPR(tmp2), + ); + a.emit_or(Size::S32, Location::GPR(tmp2), Location::GPR(tmp1)); + a.emit_mov(Size::S32, Location::GPR(tmp1), ret); + self.machine.release_temp_gpr(tmp2); + self.machine.release_temp_gpr(tmp1); + } + + Operator::F32Abs => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp = self.machine.acquire_temp_gpr().unwrap(); + a.emit_mov(Size::S32, loc, Location::GPR(tmp)); + a.emit_and( + Size::S32, + Location::Imm32(0x7fffffffu32), + Location::GPR(tmp), + ); + a.emit_mov(Size::S32, Location::GPR(tmp), ret); + self.machine.release_temp_gpr(tmp); + } + + Operator::F32Neg => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp = self.machine.acquire_temp_gpr().unwrap(); + a.emit_mov(Size::S32, loc, Location::GPR(tmp)); + a.emit_btc_gpr_imm8_32(31, tmp); + a.emit_mov(Size::S32, Location::GPR(tmp), ret); + self.machine.release_temp_gpr(tmp); + } + + Operator::F64Const { value } => self + .value_stack + .push((Location::Imm64(value.bits()), LocalOrTemp::Temp)), + Operator::F64Add => Self::emit_fp_binop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vaddsd, + ), + Operator::F64Sub => Self::emit_fp_binop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vsubsd, + ), + Operator::F64Mul => Self::emit_fp_binop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vmulsd, + ), + Operator::F64Div => Self::emit_fp_binop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vdivsd, + ), + Operator::F64Max => Self::emit_fp_binop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vmaxsd, + ), + Operator::F64Min => Self::emit_fp_binop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vminsd, + ), + Operator::F64Eq => Self::emit_fp_cmpop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vcmpeqsd, + ), + Operator::F64Ne => Self::emit_fp_cmpop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vcmpneqsd, + ), + Operator::F64Lt => Self::emit_fp_cmpop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vcmpltsd, + ), + Operator::F64Le => Self::emit_fp_cmpop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vcmplesd, + ), + Operator::F64Gt => Self::emit_fp_cmpop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vcmpgtsd, + ), + Operator::F64Ge => Self::emit_fp_cmpop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vcmpgesd, + ), + Operator::F64Nearest => Self::emit_fp_unop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vroundsd_nearest, + ), + Operator::F64Floor => Self::emit_fp_unop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vroundsd_floor, + ), + Operator::F64Ceil => Self::emit_fp_unop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vroundsd_ceil, + ), + Operator::F64Trunc => Self::emit_fp_unop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vroundsd_trunc, + ), + Operator::F64Sqrt => Self::emit_fp_unop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vsqrtsd, + ), + + Operator::F64Copysign => { + let loc_b = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let loc_a = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + let tmp1 = self.machine.acquire_temp_gpr().unwrap(); + let tmp2 = self.machine.acquire_temp_gpr().unwrap(); + let c = self.machine.acquire_temp_gpr().unwrap(); + + a.emit_mov(Size::S64, loc_a, Location::GPR(tmp1)); + a.emit_mov(Size::S64, loc_b, Location::GPR(tmp2)); + + a.emit_mov( + Size::S64, + Location::Imm64(0x7fffffffffffffffu64), + Location::GPR(c), + ); + a.emit_and(Size::S64, Location::GPR(c), Location::GPR(tmp1)); + + a.emit_mov( + Size::S64, + Location::Imm64(0x8000000000000000u64), + Location::GPR(c), + ); + a.emit_and(Size::S64, Location::GPR(c), Location::GPR(tmp2)); + + a.emit_or(Size::S64, Location::GPR(tmp2), Location::GPR(tmp1)); + a.emit_mov(Size::S64, Location::GPR(tmp1), ret); + + self.machine.release_temp_gpr(c); + self.machine.release_temp_gpr(tmp2); + self.machine.release_temp_gpr(tmp1); + } + + Operator::F64Abs => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + let tmp = self.machine.acquire_temp_gpr().unwrap(); + let c = self.machine.acquire_temp_gpr().unwrap(); + + a.emit_mov(Size::S64, loc, Location::GPR(tmp)); + a.emit_mov( + Size::S64, + Location::Imm64(0x7fffffffffffffffu64), + Location::GPR(c), + ); + a.emit_and(Size::S64, Location::GPR(c), Location::GPR(tmp)); + a.emit_mov(Size::S64, Location::GPR(tmp), ret); + + self.machine.release_temp_gpr(c); + self.machine.release_temp_gpr(tmp); + } + + Operator::F64Neg => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp = self.machine.acquire_temp_gpr().unwrap(); + a.emit_mov(Size::S64, loc, Location::GPR(tmp)); + a.emit_btc_gpr_imm8_64(63, tmp); + a.emit_mov(Size::S64, Location::GPR(tmp), ret); + self.machine.release_temp_gpr(tmp); + } + + Operator::F64PromoteF32 => Self::emit_fp_unop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vcvtss2sd, + ), + Operator::F32DemoteF64 => Self::emit_fp_unop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vcvtsd2ss, + ), + + Operator::I32ReinterpretF32 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + if loc != ret { + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + loc, + ret, + ); + } + } + Operator::F32ReinterpretI32 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + if loc != ret { + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + loc, + ret, + ); + } + } + + Operator::I64ReinterpretF64 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + if loc != ret { + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + ret, + ); + } + } + Operator::F64ReinterpretI64 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + if loc != ret { + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + ret, + ); + } + } + + Operator::I32TruncUF32 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + + a.emit_mov(Size::S32, loc, Location::XMM(tmp_in)); + Self::emit_f32_int_conv_check(a, &mut self.machine, tmp_in, -1.0, 4294967296.0); + + a.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); + a.emit_mov(Size::S32, Location::GPR(tmp_out), ret); + + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + + Operator::I32TruncSF32 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + + a.emit_mov(Size::S32, loc, Location::XMM(tmp_in)); + Self::emit_f32_int_conv_check( + a, + &mut self.machine, + tmp_in, + -2147483904.0, + 2147483648.0, + ); + + a.emit_cvttss2si_32(XMMOrMemory::XMM(tmp_in), tmp_out); + a.emit_mov(Size::S32, Location::GPR(tmp_out), ret); + + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + + Operator::I64TruncSF32 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + + a.emit_mov(Size::S32, loc, Location::XMM(tmp_in)); + Self::emit_f32_int_conv_check( + a, + &mut self.machine, + tmp_in, + -9223373136366403584.0, + 9223372036854775808.0, + ); + a.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); + a.emit_mov(Size::S64, Location::GPR(tmp_out), ret); + + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + + Operator::I64TruncUF32 => { + /* + ; movq xmm5, r15 + ; mov r15d, 1593835520u32 as i32 //float 9.22337203E+18 + ; movd xmm1, r15d + ; movd xmm2, Rd(reg as u8) + ; movd xmm3, Rd(reg as u8) + ; subss xmm2, xmm1 + ; cvttss2si Rq(reg as u8), xmm2 + ; mov r15, QWORD 0x8000000000000000u64 as i64 + ; xor r15, Rq(reg as u8) + ; cvttss2si Rq(reg as u8), xmm3 + ; ucomiss xmm3, xmm1 + ; cmovae Rq(reg as u8), r15 + ; movq r15, xmm5 + */ + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); // xmm2 + + a.emit_mov(Size::S32, loc, Location::XMM(tmp_in)); + Self::emit_f32_int_conv_check( + a, + &mut self.machine, + tmp_in, + -1.0, + 18446744073709551616.0, + ); + + let tmp = self.machine.acquire_temp_gpr().unwrap(); // r15 + let tmp_x1 = self.machine.acquire_temp_xmm().unwrap(); // xmm1 + let tmp_x2 = self.machine.acquire_temp_xmm().unwrap(); // xmm3 + + a.emit_mov( + Size::S32, + Location::Imm32(1593835520u32), + Location::GPR(tmp), + ); //float 9.22337203E+18 + a.emit_mov(Size::S32, Location::GPR(tmp), Location::XMM(tmp_x1)); + a.emit_mov(Size::S32, Location::XMM(tmp_in), Location::XMM(tmp_x2)); + a.emit_vsubss(tmp_in, XMMOrMemory::XMM(tmp_x1), tmp_in); + a.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); + a.emit_mov( + Size::S64, + Location::Imm64(0x8000000000000000u64), + Location::GPR(tmp), + ); + a.emit_xor(Size::S64, Location::GPR(tmp_out), Location::GPR(tmp)); + a.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_x2), tmp_out); + a.emit_ucomiss(XMMOrMemory::XMM(tmp_x1), tmp_x2); + a.emit_cmovae_gpr_64(tmp, tmp_out); + a.emit_mov(Size::S64, Location::GPR(tmp_out), ret); + + self.machine.release_temp_xmm(tmp_x2); + self.machine.release_temp_xmm(tmp_x1); + self.machine.release_temp_gpr(tmp); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + + Operator::I32TruncUF64 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + + a.emit_mov(Size::S64, loc, Location::XMM(tmp_in)); + Self::emit_f64_int_conv_check(a, &mut self.machine, tmp_in, -1.0, 4294967296.0); + + a.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); + a.emit_mov(Size::S32, Location::GPR(tmp_out), ret); + + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + + Operator::I32TruncSF64 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + + let real_in = match loc { + Location::Imm32(_) | Location::Imm64(_) => { + a.emit_mov(Size::S64, loc, Location::GPR(tmp_out)); + a.emit_mov(Size::S64, Location::GPR(tmp_out), Location::XMM(tmp_in)); + tmp_in + } + Location::XMM(x) => x, + _ => { + a.emit_mov(Size::S64, loc, Location::XMM(tmp_in)); + tmp_in + } + }; + + Self::emit_f64_int_conv_check( + a, + &mut self.machine, + real_in, + -2147483649.0, + 2147483648.0, + ); + + a.emit_cvttsd2si_32(XMMOrMemory::XMM(real_in), tmp_out); + a.emit_mov(Size::S32, Location::GPR(tmp_out), ret); + + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + + Operator::I64TruncSF64 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + + a.emit_mov(Size::S64, loc, Location::XMM(tmp_in)); + Self::emit_f64_int_conv_check( + a, + &mut self.machine, + tmp_in, + -9223372036854777856.0, + 9223372036854775808.0, + ); + + a.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); + a.emit_mov(Size::S64, Location::GPR(tmp_out), ret); + + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + + Operator::I64TruncUF64 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); // xmm2 + + a.emit_mov(Size::S64, loc, Location::XMM(tmp_in)); + Self::emit_f64_int_conv_check( + a, + &mut self.machine, + tmp_in, + -1.0, + 18446744073709551616.0, + ); + + let tmp = self.machine.acquire_temp_gpr().unwrap(); // r15 + let tmp_x1 = self.machine.acquire_temp_xmm().unwrap(); // xmm1 + let tmp_x2 = self.machine.acquire_temp_xmm().unwrap(); // xmm3 + + a.emit_mov( + Size::S64, + Location::Imm64(4890909195324358656u64), + Location::GPR(tmp), + ); //double 9.2233720368547758E+18 + a.emit_mov(Size::S64, Location::GPR(tmp), Location::XMM(tmp_x1)); + a.emit_mov(Size::S64, Location::XMM(tmp_in), Location::XMM(tmp_x2)); + a.emit_vsubsd(tmp_in, XMMOrMemory::XMM(tmp_x1), tmp_in); + a.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); + a.emit_mov( + Size::S64, + Location::Imm64(0x8000000000000000u64), + Location::GPR(tmp), + ); + a.emit_xor(Size::S64, Location::GPR(tmp_out), Location::GPR(tmp)); + a.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_x2), tmp_out); + a.emit_ucomisd(XMMOrMemory::XMM(tmp_x1), tmp_x2); + a.emit_cmovae_gpr_64(tmp, tmp_out); + a.emit_mov(Size::S64, Location::GPR(tmp_out), ret); + + self.machine.release_temp_xmm(tmp_x2); + self.machine.release_temp_xmm(tmp_x1); + self.machine.release_temp_gpr(tmp); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + + Operator::F32ConvertSI32 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + + a.emit_mov(Size::S32, loc, Location::GPR(tmp_in)); + a.emit_vcvtsi2ss_32(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + a.emit_mov(Size::S32, Location::XMM(tmp_out), ret); + + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } + Operator::F32ConvertUI32 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + + a.emit_mov(Size::S32, loc, Location::GPR(tmp_in)); + a.emit_vcvtsi2ss_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + a.emit_mov(Size::S32, Location::XMM(tmp_out), ret); + + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } + Operator::F32ConvertSI64 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + + a.emit_mov(Size::S64, loc, Location::GPR(tmp_in)); + a.emit_vcvtsi2ss_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + a.emit_mov(Size::S32, Location::XMM(tmp_out), ret); + + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } + Operator::F32ConvertUI64 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + let tmp = self.machine.acquire_temp_gpr().unwrap(); + + let do_convert = a.get_label(); + let end_convert = a.get_label(); + + a.emit_mov(Size::S64, loc, Location::GPR(tmp_in)); + a.emit_test_gpr_64(tmp_in); + a.emit_jmp(Condition::Signed, do_convert); + a.emit_vcvtsi2ss_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + a.emit_jmp(Condition::None, end_convert); + a.emit_label(do_convert); + a.emit_mov(Size::S64, Location::GPR(tmp_in), Location::GPR(tmp)); + a.emit_and(Size::S64, Location::Imm32(1), Location::GPR(tmp)); + a.emit_shr(Size::S64, Location::Imm8(1), Location::GPR(tmp_in)); + a.emit_or(Size::S64, Location::GPR(tmp), Location::GPR(tmp_in)); + a.emit_vcvtsi2ss_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + a.emit_vaddss(tmp_out, XMMOrMemory::XMM(tmp_out), tmp_out); + a.emit_label(end_convert); + a.emit_mov(Size::S32, Location::XMM(tmp_out), ret); + + self.machine.release_temp_gpr(tmp); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } + + Operator::F64ConvertSI32 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + + a.emit_mov(Size::S32, loc, Location::GPR(tmp_in)); + a.emit_vcvtsi2sd_32(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + a.emit_mov(Size::S64, Location::XMM(tmp_out), ret); + + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } + Operator::F64ConvertUI32 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + + a.emit_mov(Size::S32, loc, Location::GPR(tmp_in)); + a.emit_vcvtsi2sd_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + a.emit_mov(Size::S64, Location::XMM(tmp_out), ret); + + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } + Operator::F64ConvertSI64 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + + a.emit_mov(Size::S64, loc, Location::GPR(tmp_in)); + a.emit_vcvtsi2sd_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + a.emit_mov(Size::S64, Location::XMM(tmp_out), ret); + + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } + Operator::F64ConvertUI64 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + let tmp = self.machine.acquire_temp_gpr().unwrap(); + + let do_convert = a.get_label(); + let end_convert = a.get_label(); + + a.emit_mov(Size::S64, loc, Location::GPR(tmp_in)); + a.emit_test_gpr_64(tmp_in); + a.emit_jmp(Condition::Signed, do_convert); + a.emit_vcvtsi2sd_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + a.emit_jmp(Condition::None, end_convert); + a.emit_label(do_convert); + a.emit_mov(Size::S64, Location::GPR(tmp_in), Location::GPR(tmp)); + a.emit_and(Size::S64, Location::Imm32(1), Location::GPR(tmp)); + a.emit_shr(Size::S64, Location::Imm8(1), Location::GPR(tmp_in)); + a.emit_or(Size::S64, Location::GPR(tmp), Location::GPR(tmp_in)); + a.emit_vcvtsi2sd_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + a.emit_vaddsd(tmp_out, XMMOrMemory::XMM(tmp_out), tmp_out); + a.emit_label(end_convert); + a.emit_mov(Size::S64, Location::XMM(tmp_out), ret); + + self.machine.release_temp_gpr(tmp); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } + + Operator::Call { function_index } => { + let function_index = function_index as usize; + let label = self + .function_labels + .as_mut() + .unwrap() + .entry(function_index) + .or_insert_with(|| (a.get_label(), None)) + .0; + let sig_index = *self + .function_signatures + .get(FuncIndex::new(function_index)) + .unwrap(); + let sig = self.signatures.get(sig_index).unwrap(); + let param_types: SmallVec<[WpType; 8]> = + sig.params().iter().cloned().map(type_to_wp_type).collect(); + let return_types: SmallVec<[WpType; 1]> = + sig.returns().iter().cloned().map(type_to_wp_type).collect(); + + let params: SmallVec<[_; 8]> = self + .value_stack + .drain(self.value_stack.len() - param_types.len()..) + .collect(); + let released: SmallVec<[Location; 8]> = params + .iter() + .filter(|&&(_, lot)| lot == LocalOrTemp::Temp) + .map(|&(x, _)| x) + .collect(); + self.machine.release_locations_only_regs(&released); + + Self::emit_call_sysv_label( + a, + &mut self.machine, + label, + params.iter().map(|&(x, _)| x), + ); + + self.machine.release_locations_only_stack(a, &released); + + if return_types.len() > 0 { + let ret = self.machine.acquire_locations(a, &[return_types[0]], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + } + } + Operator::CallIndirect { index, table_index } => { + assert_eq!(table_index, 0); + let sig = self.signatures.get(SigIndex::new(index as usize)).unwrap(); + let param_types: SmallVec<[WpType; 8]> = + sig.params().iter().cloned().map(type_to_wp_type).collect(); + let return_types: SmallVec<[WpType; 1]> = + sig.returns().iter().cloned().map(type_to_wp_type).collect(); + + let func_index = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + let params: SmallVec<[_; 8]> = self + .value_stack + .drain(self.value_stack.len() - param_types.len()..) + .collect(); + let released: SmallVec<[Location; 8]> = params + .iter() + .filter(|&&(_, lot)| lot == LocalOrTemp::Temp) + .map(|&(x, _)| x) + .collect(); + self.machine.release_locations_only_regs(&released); + + let table_base = self.machine.acquire_temp_gpr().unwrap(); + let table_count = self.machine.acquire_temp_gpr().unwrap(); + let sigidx = self.machine.acquire_temp_gpr().unwrap(); + + a.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + match TableIndex::new(0).local_or_import(module_info) { + LocalOrImport::Local(_) => vm::Ctx::offset_tables(), + LocalOrImport::Import(_) => vm::Ctx::offset_imported_tables(), + } as i32, + ), + Location::GPR(table_base), + ); + a.emit_mov( + Size::S64, + Location::Memory(table_base, 0), + Location::GPR(table_base), + ); + a.emit_mov( + Size::S32, + Location::Memory(table_base, LocalTable::offset_count() as i32), + Location::GPR(table_count), + ); + a.emit_mov( + Size::S64, + Location::Memory(table_base, LocalTable::offset_base() as i32), + Location::GPR(table_base), + ); + a.emit_cmp(Size::S32, func_index, Location::GPR(table_count)); + a.emit_conditional_trap(Condition::BelowEqual); + a.emit_mov(Size::S64, func_index, Location::GPR(table_count)); + a.emit_imul_imm32_gpr64(vm::Anyfunc::size() as u32, table_count); + a.emit_add( + Size::S64, + Location::GPR(table_base), + Location::GPR(table_count), + ); + a.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + vm::Ctx::offset_signatures() as i32, + ), + Location::GPR(sigidx), + ); + a.emit_mov( + Size::S32, + Location::Memory(sigidx, (index * 4) as i32), + Location::GPR(sigidx), + ); + a.emit_cmp( + Size::S32, + Location::GPR(sigidx), + Location::Memory(table_count, (vm::Anyfunc::offset_sig_id() as usize) as i32), + ); + a.emit_conditional_trap(Condition::NotEqual); + + self.machine.release_temp_gpr(sigidx); + self.machine.release_temp_gpr(table_count); + self.machine.release_temp_gpr(table_base); + + if table_count != GPR::RAX { + a.emit_mov( + Size::S64, + Location::GPR(table_count), + Location::GPR(GPR::RAX), + ); + } + + Self::emit_call_sysv( + a, + &mut self.machine, + |a| { + a.emit_call_location(Location::Memory( + GPR::RAX, + (vm::Anyfunc::offset_func() as usize) as i32, + )); + }, + params.iter().map(|&(x, _)| x), + ); + + self.machine.release_locations_only_stack(a, &released); + + if return_types.len() > 0 { + let ret = self.machine.acquire_locations(a, &[return_types[0]], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + } + } + Operator::If { ty } => { + let label_end = a.get_label(); + let label_else = a.get_label(); + + let cond = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + self.control_stack.push(ControlFrame { + label: label_end, + loop_like: false, + if_else: IfElseState::If(label_else), + returns: match ty { + WpType::EmptyBlockType => smallvec![], + _ => smallvec![ty], + }, + value_stack_depth: self.value_stack.len(), + }); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_cmp, + Size::S32, + Location::Imm32(0), + cond, + ); + a.emit_jmp(Condition::Equal, label_else); + } + Operator::Else => { + let mut frame = self.control_stack.last_mut().unwrap(); + + if !was_unreachable && frame.returns.len() > 0 { + let (loc, _) = *self.value_stack.last().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + Location::GPR(GPR::RAX), + ); + } + + let released: Vec = self + .value_stack + .drain(frame.value_stack_depth..) + .filter(|&(_, lot)| lot == LocalOrTemp::Temp) + .map(|(x, _)| x) + .collect(); + self.machine.release_locations(a, &released); + + match frame.if_else { + IfElseState::If(label) => { + a.emit_jmp(Condition::None, frame.label); + a.emit_label(label); + frame.if_else = IfElseState::Else; + } + _ => unreachable!(), + } + } + Operator::Select => { + let cond = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let v_b = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let v_a = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + let end_label = a.get_label(); + let zero_label = a.get_label(); + + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_cmp, + Size::S32, + Location::Imm32(0), + cond, + ); + a.emit_jmp(Condition::Equal, zero_label); + if v_a != ret { + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + v_a, + ret, + ); + } + a.emit_jmp(Condition::None, end_label); + a.emit_label(zero_label); + if v_b != ret { + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + v_b, + ret, + ); + } + a.emit_label(end_label); + } + Operator::Block { ty } => { + self.control_stack.push(ControlFrame { + label: a.get_label(), + loop_like: false, + if_else: IfElseState::None, + returns: match ty { + WpType::EmptyBlockType => smallvec![], + _ => smallvec![ty], + }, + value_stack_depth: self.value_stack.len(), + }); + } + Operator::Loop { ty } => { + let label = a.get_label(); + self.control_stack.push(ControlFrame { + label: label, + loop_like: true, + if_else: IfElseState::None, + returns: match ty { + WpType::EmptyBlockType => smallvec![], + _ => smallvec![ty], + }, + value_stack_depth: self.value_stack.len(), + }); + a.emit_label(label); + } + Operator::Nop => {} + Operator::MemorySize { reserved } => { + let memory_index = MemoryIndex::new(reserved as usize); + let target: usize = match memory_index.local_or_import(module_info) { + LocalOrImport::Local(local_mem_index) => { + let mem_desc = &module_info.memories[local_mem_index]; + match mem_desc.memory_type() { + MemoryType::Dynamic => vmcalls::local_dynamic_memory_size as usize, + MemoryType::Static => vmcalls::local_static_memory_size as usize, + MemoryType::SharedStatic => unimplemented!(), + } + } + LocalOrImport::Import(import_mem_index) => { + let mem_desc = &module_info.imported_memories[import_mem_index].1; + match mem_desc.memory_type() { + MemoryType::Dynamic => vmcalls::imported_dynamic_memory_size as usize, + MemoryType::Static => vmcalls::imported_static_memory_size as usize, + MemoryType::SharedStatic => unimplemented!(), + } + } + }; + Self::emit_call_sysv( + a, + &mut self.machine, + |a| { + a.emit_mov( + Size::S64, + Location::Imm64(target as u64), + Location::GPR(GPR::RAX), + ); + a.emit_call_location(Location::GPR(GPR::RAX)); + }, + ::std::iter::once(Location::Imm32(memory_index.index() as u32)), + ); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + } + Operator::MemoryGrow { reserved } => { + let memory_index = MemoryIndex::new(reserved as usize); + let target: usize = match memory_index.local_or_import(module_info) { + LocalOrImport::Local(local_mem_index) => { + let mem_desc = &module_info.memories[local_mem_index]; + match mem_desc.memory_type() { + MemoryType::Dynamic => vmcalls::local_dynamic_memory_grow as usize, + MemoryType::Static => vmcalls::local_static_memory_grow as usize, + MemoryType::SharedStatic => unimplemented!(), + } + } + LocalOrImport::Import(import_mem_index) => { + let mem_desc = &module_info.imported_memories[import_mem_index].1; + match mem_desc.memory_type() { + MemoryType::Dynamic => vmcalls::imported_dynamic_memory_grow as usize, + MemoryType::Static => vmcalls::imported_static_memory_grow as usize, + MemoryType::SharedStatic => unimplemented!(), + } + } + }; + + let (param_pages, param_pages_lot) = self.value_stack.pop().unwrap(); + + if param_pages_lot == LocalOrTemp::Temp { + self.machine.release_locations_only_regs(&[param_pages]); + } + + Self::emit_call_sysv( + a, + &mut self.machine, + |a| { + a.emit_mov( + Size::S64, + Location::Imm64(target as u64), + Location::GPR(GPR::RAX), + ); + a.emit_call_location(Location::GPR(GPR::RAX)); + }, + ::std::iter::once(Location::Imm32(memory_index.index() as u32)) + .chain(::std::iter::once(param_pages)), + ); + + if param_pages_lot == LocalOrTemp::Temp { + self.machine.release_locations_only_stack(a, &[param_pages]); + } + + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + } + Operator::I32Load { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 4, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S32, + Location::Memory(addr, 0), + ret, + ); + }, + ); + } + Operator::F32Load { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 4, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S32, + Location::Memory(addr, 0), + ret, + ); + }, + ); + } + Operator::I32Load8U { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 1, + |a, m, addr| { + Self::emit_relaxed_zx_sx( + a, + m, + Assembler::emit_movzx, + Size::S8, + Location::Memory(addr, 0), + Size::S32, + ret, + ); + }, + ); + } + Operator::I32Load8S { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 1, + |a, m, addr| { + Self::emit_relaxed_zx_sx( + a, + m, + Assembler::emit_movsx, + Size::S8, + Location::Memory(addr, 0), + Size::S32, + ret, + ); + }, + ); + } + Operator::I32Load16U { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 2, + |a, m, addr| { + Self::emit_relaxed_zx_sx( + a, + m, + Assembler::emit_movzx, + Size::S16, + Location::Memory(addr, 0), + Size::S32, + ret, + ); + }, + ); + } + Operator::I32Load16S { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 2, + |a, m, addr| { + Self::emit_relaxed_zx_sx( + a, + m, + Assembler::emit_movsx, + Size::S16, + Location::Memory(addr, 0), + Size::S32, + ret, + ); + }, + ); + } + Operator::I32Store { ref memarg } => { + let target_value = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target_addr = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target_addr, + memarg.offset as usize, + 4, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S32, + target_value, + Location::Memory(addr, 0), + ); + }, + ); + } + Operator::F32Store { ref memarg } => { + let target_value = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target_addr = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target_addr, + memarg.offset as usize, + 4, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S32, + target_value, + Location::Memory(addr, 0), + ); + }, + ); + } + Operator::I32Store8 { ref memarg } => { + let target_value = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target_addr = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target_addr, + memarg.offset as usize, + 1, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S8, + target_value, + Location::Memory(addr, 0), + ); + }, + ); + } + Operator::I32Store16 { ref memarg } => { + let target_value = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target_addr = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target_addr, + memarg.offset as usize, + 2, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S16, + target_value, + Location::Memory(addr, 0), + ); + }, + ); + } + Operator::I64Load { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 8, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S64, + Location::Memory(addr, 0), + ret, + ); + }, + ); + } + Operator::F64Load { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 8, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S64, + Location::Memory(addr, 0), + ret, + ); + }, + ); + } + Operator::I64Load8U { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 1, + |a, m, addr| { + Self::emit_relaxed_zx_sx( + a, + m, + Assembler::emit_movzx, + Size::S8, + Location::Memory(addr, 0), + Size::S64, + ret, + ); + }, + ); + } + Operator::I64Load8S { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 1, + |a, m, addr| { + Self::emit_relaxed_zx_sx( + a, + m, + Assembler::emit_movsx, + Size::S8, + Location::Memory(addr, 0), + Size::S64, + ret, + ); + }, + ); + } + Operator::I64Load16U { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 2, + |a, m, addr| { + Self::emit_relaxed_zx_sx( + a, + m, + Assembler::emit_movzx, + Size::S16, + Location::Memory(addr, 0), + Size::S64, + ret, + ); + }, + ); + } + Operator::I64Load16S { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 2, + |a, m, addr| { + Self::emit_relaxed_zx_sx( + a, + m, + Assembler::emit_movsx, + Size::S16, + Location::Memory(addr, 0), + Size::S64, + ret, + ); + }, + ); + } + Operator::I64Load32U { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 4, + |a, m, addr| { + match ret { + Location::GPR(_) => {} + _ => { + a.emit_mov(Size::S64, Location::Imm64(0), ret); + } + } + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S32, + Location::Memory(addr, 0), + ret, + ); + }, + ); + } + Operator::I64Load32S { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 4, + |a, m, addr| { + Self::emit_relaxed_zx_sx( + a, + m, + Assembler::emit_movsx, + Size::S32, + Location::Memory(addr, 0), + Size::S64, + ret, + ); + }, + ); + } + Operator::I64Store { ref memarg } => { + let target_value = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target_addr = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target_addr, + memarg.offset as usize, + 8, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S64, + target_value, + Location::Memory(addr, 0), + ); + }, + ); + } + Operator::F64Store { ref memarg } => { + let target_value = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target_addr = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target_addr, + memarg.offset as usize, + 8, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S64, + target_value, + Location::Memory(addr, 0), + ); + }, + ); + } + Operator::I64Store8 { ref memarg } => { + let target_value = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target_addr = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target_addr, + memarg.offset as usize, + 1, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S8, + target_value, + Location::Memory(addr, 0), + ); + }, + ); + } + Operator::I64Store16 { ref memarg } => { + let target_value = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target_addr = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target_addr, + memarg.offset as usize, + 2, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S16, + target_value, + Location::Memory(addr, 0), + ); + }, + ); + } + Operator::I64Store32 { ref memarg } => { + let target_value = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target_addr = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target_addr, + memarg.offset as usize, + 4, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S32, + target_value, + Location::Memory(addr, 0), + ); + }, + ); + } + Operator::Unreachable => { + a.emit_ud2(); + self.unreachable_depth = 1; + } + Operator::Return => { + let frame = &self.control_stack[0]; + if frame.returns.len() > 0 { + assert_eq!(frame.returns.len(), 1); + let (loc, _) = *self.value_stack.last().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + Location::GPR(GPR::RAX), + ); + } + let released: Vec = self.value_stack[frame.value_stack_depth..] + .iter() + .filter(|&&(_, lot)| lot == LocalOrTemp::Temp) + .map(|&(x, _)| x) + .collect(); + self.machine.release_locations_keep_state(a, &released); + a.emit_jmp(Condition::None, frame.label); + self.unreachable_depth = 1; + } + Operator::Br { relative_depth } => { + let frame = + &self.control_stack[self.control_stack.len() - 1 - (relative_depth as usize)]; + if !frame.loop_like && frame.returns.len() > 0 { + assert_eq!(frame.returns.len(), 1); + let (loc, _) = *self.value_stack.last().unwrap(); + a.emit_mov(Size::S64, loc, Location::GPR(GPR::RAX)); + } + let released: Vec = self.value_stack[frame.value_stack_depth..] + .iter() + .filter(|&&(_, lot)| lot == LocalOrTemp::Temp) + .map(|&(x, _)| x) + .collect(); + self.machine.release_locations_keep_state(a, &released); + a.emit_jmp(Condition::None, frame.label); + self.unreachable_depth = 1; + } + Operator::BrIf { relative_depth } => { + let after = a.get_label(); + let cond = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_cmp, + Size::S32, + Location::Imm32(0), + cond, + ); + a.emit_jmp(Condition::Equal, after); + + let frame = + &self.control_stack[self.control_stack.len() - 1 - (relative_depth as usize)]; + if !frame.loop_like && frame.returns.len() > 0 { + assert_eq!(frame.returns.len(), 1); + let (loc, _) = *self.value_stack.last().unwrap(); + a.emit_mov(Size::S64, loc, Location::GPR(GPR::RAX)); + } + let released: Vec = self.value_stack[frame.value_stack_depth..] + .iter() + .filter(|&&(_, lot)| lot == LocalOrTemp::Temp) + .map(|&(x, _)| x) + .collect(); + self.machine.release_locations_keep_state(a, &released); + a.emit_jmp(Condition::None, frame.label); + + a.emit_label(after); + } + Operator::BrTable { ref table } => { + let (targets, default_target) = table.read_table().unwrap(); + let cond = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let mut table = vec![0usize; targets.len()]; + let default_br = a.get_label(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_cmp, + Size::S32, + Location::Imm32(targets.len() as u32), + cond, + ); + a.emit_jmp(Condition::AboveEqual, default_br); + + a.emit_mov( + Size::S64, + Location::Imm64(table.as_ptr() as usize as u64), + Location::GPR(GPR::RCX), + ); + a.emit_mov(Size::S32, cond, Location::GPR(GPR::RDX)); + a.emit_shl(Size::S32, Location::Imm8(3), Location::GPR(GPR::RDX)); + a.emit_add(Size::S64, Location::GPR(GPR::RCX), Location::GPR(GPR::RDX)); + a.emit_jmp_location(Location::Memory(GPR::RDX, 0)); + + for (i, target) in targets.iter().enumerate() { + let AssemblyOffset(offset) = a.offset(); + table[i] = offset; + let frame = + &self.control_stack[self.control_stack.len() - 1 - (*target as usize)]; + if !frame.loop_like && frame.returns.len() > 0 { + assert_eq!(frame.returns.len(), 1); + let (loc, _) = *self.value_stack.last().unwrap(); + a.emit_mov(Size::S64, loc, Location::GPR(GPR::RAX)); + } + let released: Vec = self.value_stack[frame.value_stack_depth..] + .iter() + .filter(|&&(_, lot)| lot == LocalOrTemp::Temp) + .map(|&(x, _)| x) + .collect(); + self.machine.release_locations_keep_state(a, &released); + a.emit_jmp(Condition::None, frame.label); + } + a.emit_label(default_br); + + { + let frame = &self.control_stack + [self.control_stack.len() - 1 - (default_target as usize)]; + if !frame.loop_like && frame.returns.len() > 0 { + assert_eq!(frame.returns.len(), 1); + let (loc, _) = *self.value_stack.last().unwrap(); + a.emit_mov(Size::S64, loc, Location::GPR(GPR::RAX)); + } + let released: Vec = self.value_stack[frame.value_stack_depth..] + .iter() + .filter(|&&(_, lot)| lot == LocalOrTemp::Temp) + .map(|&(x, _)| x) + .collect(); + self.machine.release_locations_keep_state(a, &released); + a.emit_jmp(Condition::None, frame.label); + } + + self.br_table_data.as_mut().unwrap().push(table); + self.unreachable_depth = 1; + } + Operator::Drop => { + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + } + Operator::End => { + let frame = self.control_stack.pop().unwrap(); + + if !was_unreachable && frame.returns.len() > 0 { + let (loc, _) = *self.value_stack.last().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + Location::GPR(GPR::RAX), + ); + } + + if self.control_stack.len() == 0 { + a.emit_label(frame.label); + self.machine.finalize_locals(a, &self.locals); + a.emit_mov(Size::S64, Location::GPR(GPR::RBP), Location::GPR(GPR::RSP)); + a.emit_pop(Size::S64, Location::GPR(GPR::RBP)); + a.emit_ret(); + } else { + let released: Vec = self + .value_stack + .drain(frame.value_stack_depth..) + .filter(|&(_, lot)| lot == LocalOrTemp::Temp) + .map(|(x, _)| x) + .collect(); + self.machine.release_locations(a, &released); + + if !frame.loop_like { + a.emit_label(frame.label); + } + + if let IfElseState::If(label) = frame.if_else { + a.emit_label(label); + } + + if frame.returns.len() > 0 { + assert_eq!(frame.returns.len(), 1); + let loc = self.machine.acquire_locations(a, &frame.returns, false)[0]; + a.emit_mov(Size::S64, Location::GPR(GPR::RAX), loc); + self.value_stack.push((loc, LocalOrTemp::Temp)); + } + } + } + _ => { + panic!("not yet implemented: {:?}", op); + } + } + + Ok(()) + } +} + +fn type_to_wp_type(ty: Type) -> WpType { + match ty { + Type::I32 => WpType::I32, + Type::I64 => WpType::I64, + Type::F32 => WpType::F32, + Type::F64 => WpType::F64, + } +} + +fn get_location_released( + a: &mut Assembler, + m: &mut Machine, + (loc, lot): (Location, LocalOrTemp), +) -> Location { + if lot == LocalOrTemp::Temp { + m.release_locations(a, &[loc]); + } + loc +} + +fn sort_call_movs(movs: &mut [(Location, GPR)]) { + for i in 0..movs.len() { + for j in (i + 1)..movs.len() { + if let Location::GPR(src_gpr) = movs[j].0 { + if src_gpr == movs[i].1 { + movs.swap(i, j); + } + } + } + } +} diff --git a/lib/singlepass-backend/src/emitter_x64.rs b/lib/singlepass-backend/src/emitter_x64.rs new file mode 100644 index 000000000..889b1099d --- /dev/null +++ b/lib/singlepass-backend/src/emitter_x64.rs @@ -0,0 +1,956 @@ +use dynasmrt::{x64::Assembler, AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi}; + +#[repr(u8)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub enum GPR { + RAX, + RCX, + RDX, + RBX, + RSP, + RBP, + RSI, + RDI, + R8, + R9, + R10, + R11, + R12, + R13, + R14, + R15, +} + +#[repr(u8)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub enum XMM { + XMM0, + XMM1, + XMM2, + XMM3, + XMM4, + XMM5, + XMM6, + XMM7, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub enum Location { + Imm8(u8), + Imm32(u32), + Imm64(u64), + GPR(GPR), + XMM(XMM), + Memory(GPR, i32), +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Condition { + None, + Above, + AboveEqual, + Below, + BelowEqual, + Greater, + GreaterEqual, + Less, + LessEqual, + Equal, + NotEqual, + Signed, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Size { + S8, + S16, + S32, + S64, +} + +#[derive(Copy, Clone, Debug)] +#[allow(dead_code)] +pub enum XMMOrMemory { + XMM(XMM), + Memory(GPR, i32), +} + +#[derive(Copy, Clone, Debug)] +#[allow(dead_code)] +pub enum GPROrMemory { + GPR(GPR), + Memory(GPR, i32), +} + +pub trait Emitter { + type Label; + type Offset; + + fn get_label(&mut self) -> Self::Label; + fn get_offset(&mut self) -> Self::Offset; + + fn emit_label(&mut self, label: Self::Label); + + fn emit_mov(&mut self, sz: Size, src: Location, dst: Location); + fn emit_lea(&mut self, sz: Size, src: Location, dst: Location); + fn emit_lea_label(&mut self, label: Self::Label, dst: Location); + fn emit_cdq(&mut self); + fn emit_cqo(&mut self); + fn emit_xor(&mut self, sz: Size, src: Location, dst: Location); + fn emit_jmp(&mut self, condition: Condition, label: Self::Label); + fn emit_jmp_location(&mut self, loc: Location); + fn emit_conditional_trap(&mut self, condition: Condition); + fn emit_set(&mut self, condition: Condition, dst: GPR); + fn emit_push(&mut self, sz: Size, src: Location); + fn emit_pop(&mut self, sz: Size, dst: Location); + fn emit_cmp(&mut self, sz: Size, left: Location, right: Location); + fn emit_add(&mut self, sz: Size, src: Location, dst: Location); + fn emit_sub(&mut self, sz: Size, src: Location, dst: Location); + fn emit_imul(&mut self, sz: Size, src: Location, dst: Location); + fn emit_imul_imm32_gpr64(&mut self, src: u32, dst: GPR); + fn emit_div(&mut self, sz: Size, divisor: Location); + fn emit_idiv(&mut self, sz: Size, divisor: Location); + fn emit_shl(&mut self, sz: Size, src: Location, dst: Location); + fn emit_shr(&mut self, sz: Size, src: Location, dst: Location); + fn emit_sar(&mut self, sz: Size, src: Location, dst: Location); + fn emit_rol(&mut self, sz: Size, src: Location, dst: Location); + fn emit_ror(&mut self, sz: Size, src: Location, dst: Location); + fn emit_and(&mut self, sz: Size, src: Location, dst: Location); + fn emit_or(&mut self, sz: Size, src: Location, dst: Location); + fn emit_lzcnt(&mut self, sz: Size, src: Location, dst: Location); + fn emit_tzcnt(&mut self, sz: Size, src: Location, dst: Location); + fn emit_popcnt(&mut self, sz: Size, src: Location, dst: Location); + fn emit_movzx(&mut self, sz_src: Size, src: Location, sz_dst: Size, dst: Location); + fn emit_movsx(&mut self, sz_src: Size, src: Location, sz_dst: Size, dst: Location); + + fn emit_btc_gpr_imm8_32(&mut self, src: u8, dst: GPR); + fn emit_btc_gpr_imm8_64(&mut self, src: u8, dst: GPR); + + fn emit_cmovae_gpr_32(&mut self, src: GPR, dst: GPR); + fn emit_cmovae_gpr_64(&mut self, src: GPR, dst: GPR); + + fn emit_vaddss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vaddsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vsubss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vsubsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vmulss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vmulsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vdivss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vdivsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vmaxss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vmaxsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vminss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vminsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vcmpeqss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vcmpeqsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vcmpneqss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vcmpneqsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vcmpltss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vcmpltsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vcmpless(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vcmplesd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vcmpgtss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vcmpgtsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vcmpgess(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vcmpgesd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vsqrtss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vsqrtsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vroundss_nearest(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vroundss_floor(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vroundss_ceil(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vroundss_trunc(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vroundsd_nearest(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vroundsd_floor(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vroundsd_ceil(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vroundsd_trunc(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vcvtss2sd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vcvtsd2ss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_ucomiss(&mut self, src: XMMOrMemory, dst: XMM); + fn emit_ucomisd(&mut self, src: XMMOrMemory, dst: XMM); + + fn emit_cvttss2si_32(&mut self, src: XMMOrMemory, dst: GPR); + fn emit_cvttss2si_64(&mut self, src: XMMOrMemory, dst: GPR); + fn emit_cvttsd2si_32(&mut self, src: XMMOrMemory, dst: GPR); + fn emit_cvttsd2si_64(&mut self, src: XMMOrMemory, dst: GPR); + + fn emit_vcvtsi2ss_32(&mut self, src1: XMM, src2: GPROrMemory, dst: XMM); + fn emit_vcvtsi2ss_64(&mut self, src1: XMM, src2: GPROrMemory, dst: XMM); + fn emit_vcvtsi2sd_32(&mut self, src1: XMM, src2: GPROrMemory, dst: XMM); + fn emit_vcvtsi2sd_64(&mut self, src1: XMM, src2: GPROrMemory, dst: XMM); + + fn emit_test_gpr_64(&mut self, reg: GPR); + + fn emit_ud2(&mut self); + fn emit_ret(&mut self); + fn emit_call_label(&mut self, label: Self::Label); + fn emit_call_location(&mut self, loc: Location); + + fn emit_bkpt(&mut self); +} + +macro_rules! unop_gpr { + ($ins:ident, $assembler:tt, $sz:expr, $loc:expr, $otherwise:block) => { + match ($sz, $loc) { + (Size::S32, Location::GPR(loc)) => { + dynasm!($assembler ; $ins Rd(loc as u8)); + }, + (Size::S64, Location::GPR(loc)) => { + dynasm!($assembler ; $ins Rq(loc as u8)); + }, + _ => $otherwise + } + }; +} + +macro_rules! unop_mem { + ($ins:ident, $assembler:tt, $sz:expr, $loc:expr, $otherwise:block) => { + match ($sz, $loc) { + (Size::S32, Location::Memory(loc, disp)) => { + dynasm!($assembler ; $ins DWORD [Rq(loc as u8) + disp] ); + }, + (Size::S64, Location::Memory(loc, disp)) => { + dynasm!($assembler ; $ins QWORD [Rq(loc as u8) + disp] ); + }, + _ => $otherwise + } + }; +} + +macro_rules! unop_gpr_or_mem { + ($ins:ident, $assembler:tt, $sz:expr, $loc:expr, $otherwise:block) => { + unop_gpr!($ins, $assembler, $sz, $loc, { + unop_mem!($ins, $assembler, $sz, $loc, $otherwise) + }) + }; +} + +macro_rules! binop_imm32_gpr { + ($ins:ident, $assembler:tt, $sz:expr, $src:expr, $dst:expr, $otherwise:block) => { + match ($sz, $src, $dst) { + (Size::S32, Location::Imm32(src), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rd(dst as u8), src as i32); // IMM32_2GPR + }, + (Size::S64, Location::Imm32(src), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rq(dst as u8), src as i32); // IMM32_2GPR + }, + _ => $otherwise + } + }; +} + +macro_rules! binop_imm32_mem { + ($ins:ident, $assembler:tt, $sz:expr, $src:expr, $dst:expr, $otherwise:block) => { + match ($sz, $src, $dst) { + (Size::S32, Location::Imm32(src), Location::Memory(dst, disp)) => { + dynasm!($assembler ; $ins DWORD [Rq(dst as u8) + disp], src as i32); + }, + (Size::S64, Location::Imm32(src), Location::Memory(dst, disp)) => { + dynasm!($assembler ; $ins QWORD [Rq(dst as u8) + disp], src as i32); + }, + _ => $otherwise + } + }; +} + +macro_rules! binop_imm64_gpr { + ($ins:ident, $assembler:tt, $sz:expr, $src:expr, $dst:expr, $otherwise:block) => { + match ($sz, $src, $dst) { + (Size::S64, Location::Imm64(src), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rq(dst as u8), QWORD src as i64); // IMM32_2GPR + }, + _ => $otherwise + } + }; +} + +macro_rules! binop_gpr_gpr { + ($ins:ident, $assembler:tt, $sz:expr, $src:expr, $dst:expr, $otherwise:block) => { + match ($sz, $src, $dst) { + (Size::S32, Location::GPR(src), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rd(dst as u8), Rd(src as u8)); // GPR2GPR + }, + (Size::S64, Location::GPR(src), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rq(dst as u8), Rq(src as u8)); // GPR2GPR + }, + _ => $otherwise + } + }; +} + +macro_rules! binop_gpr_mem { + ($ins:ident, $assembler:tt, $sz:expr, $src:expr, $dst:expr, $otherwise:block) => { + match ($sz, $src, $dst) { + (Size::S32, Location::GPR(src), Location::Memory(dst, disp)) => { + dynasm!($assembler ; $ins [Rq(dst as u8) + disp], Rd(src as u8)); // GPR2MEM + }, + (Size::S64, Location::GPR(src), Location::Memory(dst, disp)) => { + dynasm!($assembler ; $ins [Rq(dst as u8) + disp], Rq(src as u8)); // GPR2MEM + }, + _ => $otherwise + } + }; +} + +macro_rules! binop_mem_gpr { + ($ins:ident, $assembler:tt, $sz:expr, $src:expr, $dst:expr, $otherwise:block) => { + match ($sz, $src, $dst) { + (Size::S32, Location::Memory(src, disp), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rd(dst as u8), [Rq(src as u8) + disp]); // MEM2GPR + }, + (Size::S64, Location::Memory(src, disp), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rq(dst as u8), [Rq(src as u8) + disp]); // MEM2GPR + }, + _ => $otherwise + } + }; +} + +macro_rules! binop_all_nofp { + ($ins:ident, $assembler:tt, $sz:expr, $src:expr, $dst:expr, $otherwise:block) => { + binop_imm32_gpr!($ins, $assembler, $sz, $src, $dst, { + binop_imm32_mem!($ins, $assembler, $sz, $src, $dst, { + binop_gpr_gpr!($ins, $assembler, $sz, $src, $dst, { + binop_gpr_mem!($ins, $assembler, $sz, $src, $dst, { + binop_mem_gpr!($ins, $assembler, $sz, $src, $dst, $otherwise) + }) + }) + }) + }) + }; +} + +macro_rules! binop_shift { + ($ins:ident, $assembler:tt, $sz:expr, $src:expr, $dst:expr, $otherwise:block) => { + match ($sz, $src, $dst) { + (Size::S32, Location::GPR(GPR::RCX), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rd(dst as u8), cl); + }, + (Size::S32, Location::GPR(GPR::RCX), Location::Memory(dst, disp)) => { + dynasm!($assembler ; $ins DWORD [Rq(dst as u8) + disp], cl); + }, + (Size::S32, Location::Imm8(imm), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rd(dst as u8), imm as i8); + }, + (Size::S32, Location::Imm8(imm), Location::Memory(dst, disp)) => { + dynasm!($assembler ; $ins DWORD [Rq(dst as u8) + disp], imm as i8); + }, + (Size::S64, Location::GPR(GPR::RCX), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rq(dst as u8), cl); + }, + (Size::S64, Location::GPR(GPR::RCX), Location::Memory(dst, disp)) => { + dynasm!($assembler ; $ins QWORD [Rq(dst as u8) + disp], cl); + }, + (Size::S64, Location::Imm8(imm), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rq(dst as u8), imm as i8); + }, + (Size::S64, Location::Imm8(imm), Location::Memory(dst, disp)) => { + dynasm!($assembler ; $ins QWORD [Rq(dst as u8) + disp], imm as i8); + }, + _ => $otherwise + } + } +} + +macro_rules! jmp_op { + ($ins:ident, $assembler:tt, $label:ident) => { + dynasm!($assembler ; $ins =>$label); + } +} + +macro_rules! trap_op { + ($ins:ident, $assembler:tt) => { + dynasm!($assembler + ; $ins >trap + ; jmp >after + ; trap: + ; ud2 + ; after: + ); + } +} + +macro_rules! avx_fn { + ($ins:ident, $name:ident) => { + fn $name(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM) { + // Dynasm bug: AVX instructions are not encoded correctly. + match src2 { + XMMOrMemory::XMM(x) => match src1 { + XMM::XMM0 => dynasm!(self ; $ins Rx((dst as u8)), xmm0, Rx((x as u8))), + XMM::XMM1 => dynasm!(self ; $ins Rx((dst as u8)), xmm1, Rx((x as u8))), + XMM::XMM2 => dynasm!(self ; $ins Rx((dst as u8)), xmm2, Rx((x as u8))), + XMM::XMM3 => dynasm!(self ; $ins Rx((dst as u8)), xmm3, Rx((x as u8))), + XMM::XMM4 => dynasm!(self ; $ins Rx((dst as u8)), xmm4, Rx((x as u8))), + XMM::XMM5 => dynasm!(self ; $ins Rx((dst as u8)), xmm5, Rx((x as u8))), + XMM::XMM6 => dynasm!(self ; $ins Rx((dst as u8)), xmm6, Rx((x as u8))), + XMM::XMM7 => dynasm!(self ; $ins Rx((dst as u8)), xmm7, Rx((x as u8))), + }, + XMMOrMemory::Memory(base, disp) => match src1 { + XMM::XMM0 => dynasm!(self ; $ins Rx((dst as u8)), xmm0, [Rq((base as u8)) + disp]), + XMM::XMM1 => dynasm!(self ; $ins Rx((dst as u8)), xmm1, [Rq((base as u8)) + disp]), + XMM::XMM2 => dynasm!(self ; $ins Rx((dst as u8)), xmm2, [Rq((base as u8)) + disp]), + XMM::XMM3 => dynasm!(self ; $ins Rx((dst as u8)), xmm3, [Rq((base as u8)) + disp]), + XMM::XMM4 => dynasm!(self ; $ins Rx((dst as u8)), xmm4, [Rq((base as u8)) + disp]), + XMM::XMM5 => dynasm!(self ; $ins Rx((dst as u8)), xmm5, [Rq((base as u8)) + disp]), + XMM::XMM6 => dynasm!(self ; $ins Rx((dst as u8)), xmm6, [Rq((base as u8)) + disp]), + XMM::XMM7 => dynasm!(self ; $ins Rx((dst as u8)), xmm7, [Rq((base as u8)) + disp]), + }, + } + } + } +} + +macro_rules! avx_i2f_64_fn { + ($ins:ident, $name:ident) => { + fn $name(&mut self, src1: XMM, src2: GPROrMemory, dst: XMM) { + match src2 { + GPROrMemory::GPR(x) => match src1 { + XMM::XMM0 => dynasm!(self ; $ins Rx((dst as u8)), xmm0, Rq((x as u8))), + XMM::XMM1 => dynasm!(self ; $ins Rx((dst as u8)), xmm1, Rq((x as u8))), + XMM::XMM2 => dynasm!(self ; $ins Rx((dst as u8)), xmm2, Rq((x as u8))), + XMM::XMM3 => dynasm!(self ; $ins Rx((dst as u8)), xmm3, Rq((x as u8))), + XMM::XMM4 => dynasm!(self ; $ins Rx((dst as u8)), xmm4, Rq((x as u8))), + XMM::XMM5 => dynasm!(self ; $ins Rx((dst as u8)), xmm5, Rq((x as u8))), + XMM::XMM6 => dynasm!(self ; $ins Rx((dst as u8)), xmm6, Rq((x as u8))), + XMM::XMM7 => dynasm!(self ; $ins Rx((dst as u8)), xmm7, Rq((x as u8))), + }, + GPROrMemory::Memory(base, disp) => match src1 { + XMM::XMM0 => dynasm!(self ; $ins Rx((dst as u8)), xmm0, QWORD [Rq((base as u8)) + disp]), + XMM::XMM1 => dynasm!(self ; $ins Rx((dst as u8)), xmm1, QWORD [Rq((base as u8)) + disp]), + XMM::XMM2 => dynasm!(self ; $ins Rx((dst as u8)), xmm2, QWORD [Rq((base as u8)) + disp]), + XMM::XMM3 => dynasm!(self ; $ins Rx((dst as u8)), xmm3, QWORD [Rq((base as u8)) + disp]), + XMM::XMM4 => dynasm!(self ; $ins Rx((dst as u8)), xmm4, QWORD [Rq((base as u8)) + disp]), + XMM::XMM5 => dynasm!(self ; $ins Rx((dst as u8)), xmm5, QWORD [Rq((base as u8)) + disp]), + XMM::XMM6 => dynasm!(self ; $ins Rx((dst as u8)), xmm6, QWORD [Rq((base as u8)) + disp]), + XMM::XMM7 => dynasm!(self ; $ins Rx((dst as u8)), xmm7, QWORD [Rq((base as u8)) + disp]), + }, + } + } + } +} + +macro_rules! avx_i2f_32_fn { + ($ins:ident, $name:ident) => { + fn $name(&mut self, src1: XMM, src2: GPROrMemory, dst: XMM) { + match src2 { + GPROrMemory::GPR(x) => match src1 { + XMM::XMM0 => dynasm!(self ; $ins Rx((dst as u8)), xmm0, Rd((x as u8))), + XMM::XMM1 => dynasm!(self ; $ins Rx((dst as u8)), xmm1, Rd((x as u8))), + XMM::XMM2 => dynasm!(self ; $ins Rx((dst as u8)), xmm2, Rd((x as u8))), + XMM::XMM3 => dynasm!(self ; $ins Rx((dst as u8)), xmm3, Rd((x as u8))), + XMM::XMM4 => dynasm!(self ; $ins Rx((dst as u8)), xmm4, Rd((x as u8))), + XMM::XMM5 => dynasm!(self ; $ins Rx((dst as u8)), xmm5, Rd((x as u8))), + XMM::XMM6 => dynasm!(self ; $ins Rx((dst as u8)), xmm6, Rd((x as u8))), + XMM::XMM7 => dynasm!(self ; $ins Rx((dst as u8)), xmm7, Rd((x as u8))), + }, + GPROrMemory::Memory(base, disp) => match src1 { + XMM::XMM0 => dynasm!(self ; $ins Rx((dst as u8)), xmm0, DWORD [Rq((base as u8)) + disp]), + XMM::XMM1 => dynasm!(self ; $ins Rx((dst as u8)), xmm1, DWORD [Rq((base as u8)) + disp]), + XMM::XMM2 => dynasm!(self ; $ins Rx((dst as u8)), xmm2, DWORD [Rq((base as u8)) + disp]), + XMM::XMM3 => dynasm!(self ; $ins Rx((dst as u8)), xmm3, DWORD [Rq((base as u8)) + disp]), + XMM::XMM4 => dynasm!(self ; $ins Rx((dst as u8)), xmm4, DWORD [Rq((base as u8)) + disp]), + XMM::XMM5 => dynasm!(self ; $ins Rx((dst as u8)), xmm5, DWORD [Rq((base as u8)) + disp]), + XMM::XMM6 => dynasm!(self ; $ins Rx((dst as u8)), xmm6, DWORD [Rq((base as u8)) + disp]), + XMM::XMM7 => dynasm!(self ; $ins Rx((dst as u8)), xmm7, DWORD [Rq((base as u8)) + disp]), + }, + } + } + } +} + +macro_rules! avx_round_fn { + ($ins:ident, $name:ident, $mode:expr) => { + fn $name(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM) { + match src2 { + XMMOrMemory::XMM(x) => dynasm!(self ; $ins Rx((dst as u8)), Rx((src1 as u8)), Rx((x as u8)), $mode), + XMMOrMemory::Memory(base, disp) => dynasm!(self ; $ins Rx((dst as u8)), Rx((src1 as u8)), [Rq((base as u8)) + disp], $mode), + } + } + } +} + +impl Emitter for Assembler { + type Label = DynamicLabel; + type Offset = AssemblyOffset; + + fn get_label(&mut self) -> DynamicLabel { + self.new_dynamic_label() + } + + fn get_offset(&mut self) -> AssemblyOffset { + self.offset() + } + + fn emit_label(&mut self, label: Self::Label) { + dynasm!(self ; => label); + } + + fn emit_mov(&mut self, sz: Size, src: Location, dst: Location) { + binop_all_nofp!(mov, self, sz, src, dst, { + binop_imm64_gpr!(mov, self, sz, src, dst, { + match (sz, src, dst) { + (Size::S8, Location::GPR(src), Location::Memory(dst, disp)) => { + dynasm!(self ; mov [Rq(dst as u8) + disp], Rb(src as u8)); + } + (Size::S8, Location::Memory(src, disp), Location::GPR(dst)) => { + dynasm!(self ; mov Rb(dst as u8), [Rq(src as u8) + disp]); + } + (Size::S8, Location::Imm32(src), Location::GPR(dst)) => { + dynasm!(self ; mov Rb(dst as u8), src as i8); + } + (Size::S8, Location::Imm64(src), Location::GPR(dst)) => { + dynasm!(self ; mov Rb(dst as u8), src as i8); + } + (Size::S8, Location::Imm32(src), Location::Memory(dst, disp)) => { + dynasm!(self ; mov BYTE [Rq(dst as u8) + disp], src as i8); + } + (Size::S8, Location::Imm64(src), Location::Memory(dst, disp)) => { + dynasm!(self ; mov BYTE [Rq(dst as u8) + disp], src as i8); + } + (Size::S16, Location::GPR(src), Location::Memory(dst, disp)) => { + dynasm!(self ; mov [Rq(dst as u8) + disp], Rw(src as u8)); + } + (Size::S16, Location::Memory(src, disp), Location::GPR(dst)) => { + dynasm!(self ; mov Rw(dst as u8), [Rq(src as u8) + disp]); + } + (Size::S16, Location::Imm32(src), Location::GPR(dst)) => { + dynasm!(self ; mov Rw(dst as u8), src as i16); + } + (Size::S16, Location::Imm64(src), Location::GPR(dst)) => { + dynasm!(self ; mov Rw(dst as u8), src as i16); + } + (Size::S16, Location::Imm32(src), Location::Memory(dst, disp)) => { + dynasm!(self ; mov WORD [Rq(dst as u8) + disp], src as i16); + } + (Size::S16, Location::Imm64(src), Location::Memory(dst, disp)) => { + dynasm!(self ; mov WORD [Rq(dst as u8) + disp], src as i16); + } + (Size::S32, Location::Imm64(src), Location::GPR(dst)) => { + dynasm!(self ; mov Rd(dst as u8), src as i32); + } + (Size::S32, Location::Imm64(src), Location::Memory(dst, disp)) => { + dynasm!(self ; mov DWORD [Rq(dst as u8) + disp], src as i32); + } + (Size::S32, Location::GPR(src), Location::XMM(dst)) => { + dynasm!(self ; movd Rx(dst as u8), Rd(src as u8)); + } + (Size::S32, Location::XMM(src), Location::GPR(dst)) => { + dynasm!(self ; movd Rd(dst as u8), Rx(src as u8)); + } + (Size::S32, Location::Memory(src, disp), Location::XMM(dst)) => { + dynasm!(self ; movd Rx(dst as u8), [Rq(src as u8) + disp]); + } + (Size::S32, Location::XMM(src), Location::Memory(dst, disp)) => { + dynasm!(self ; movd [Rq(dst as u8) + disp], Rx(src as u8)); + } + + (Size::S64, Location::GPR(src), Location::XMM(dst)) => { + dynasm!(self ; movq Rx(dst as u8), Rq(src as u8)); + } + (Size::S64, Location::XMM(src), Location::GPR(dst)) => { + dynasm!(self ; movq Rq(dst as u8), Rx(src as u8)); + } + (Size::S64, Location::Memory(src, disp), Location::XMM(dst)) => { + dynasm!(self ; movq Rx(dst as u8), [Rq(src as u8) + disp]); + } + (Size::S64, Location::XMM(src), Location::Memory(dst, disp)) => { + dynasm!(self ; movq [Rq(dst as u8) + disp], Rx(src as u8)); + } + (_, Location::XMM(src), Location::XMM(dst)) => { + dynasm!(self ; movq Rx(dst as u8), Rx(src as u8)); + } + + _ => panic!("MOV {:?} {:?} {:?}", sz, src, dst), + } + }) + }); + } + fn emit_lea(&mut self, sz: Size, src: Location, dst: Location) { + match (sz, src, dst) { + (Size::S32, Location::Memory(src, disp), Location::GPR(dst)) => { + dynasm!(self ; lea Rd(dst as u8), [Rq(src as u8) + disp]); + } + (Size::S64, Location::Memory(src, disp), Location::GPR(dst)) => { + dynasm!(self ; lea Rq(dst as u8), [Rq(src as u8) + disp]); + } + _ => unreachable!(), + } + } + fn emit_lea_label(&mut self, label: Self::Label, dst: Location) { + match dst { + Location::GPR(x) => { + dynasm!(self ; lea Rq(x as u8), [=>label]); + } + _ => unreachable!(), + } + } + fn emit_cdq(&mut self) { + dynasm!(self ; cdq); + } + fn emit_cqo(&mut self) { + dynasm!(self ; cqo); + } + fn emit_xor(&mut self, sz: Size, src: Location, dst: Location) { + binop_all_nofp!(xor, self, sz, src, dst, { unreachable!() }); + } + fn emit_jmp(&mut self, condition: Condition, label: Self::Label) { + match condition { + Condition::None => jmp_op!(jmp, self, label), + Condition::Above => jmp_op!(ja, self, label), + Condition::AboveEqual => jmp_op!(jae, self, label), + Condition::Below => jmp_op!(jb, self, label), + Condition::BelowEqual => jmp_op!(jbe, self, label), + Condition::Greater => jmp_op!(jg, self, label), + Condition::GreaterEqual => jmp_op!(jge, self, label), + Condition::Less => jmp_op!(jl, self, label), + Condition::LessEqual => jmp_op!(jle, self, label), + Condition::Equal => jmp_op!(je, self, label), + Condition::NotEqual => jmp_op!(jne, self, label), + Condition::Signed => jmp_op!(js, self, label), + } + } + fn emit_jmp_location(&mut self, loc: Location) { + match loc { + Location::GPR(x) => dynasm!(self ; jmp Rq(x as u8)), + Location::Memory(base, disp) => dynasm!(self ; jmp QWORD [Rq(base as u8) + disp]), + _ => unreachable!(), + } + } + fn emit_conditional_trap(&mut self, condition: Condition) { + match condition { + Condition::None => trap_op!(jmp, self), + Condition::Above => trap_op!(ja, self), + Condition::AboveEqual => trap_op!(jae, self), + Condition::Below => trap_op!(jb, self), + Condition::BelowEqual => trap_op!(jbe, self), + Condition::Greater => trap_op!(jg, self), + Condition::GreaterEqual => trap_op!(jge, self), + Condition::Less => trap_op!(jl, self), + Condition::LessEqual => trap_op!(jle, self), + Condition::Equal => trap_op!(je, self), + Condition::NotEqual => trap_op!(jne, self), + Condition::Signed => trap_op!(js, self), + } + } + fn emit_set(&mut self, condition: Condition, dst: GPR) { + match condition { + Condition::Above => dynasm!(self ; seta Rb(dst as u8)), + Condition::AboveEqual => dynasm!(self ; setae Rb(dst as u8)), + Condition::Below => dynasm!(self ; setb Rb(dst as u8)), + Condition::BelowEqual => dynasm!(self ; setbe Rb(dst as u8)), + Condition::Greater => dynasm!(self ; setg Rb(dst as u8)), + Condition::GreaterEqual => dynasm!(self ; setge Rb(dst as u8)), + Condition::Less => dynasm!(self ; setl Rb(dst as u8)), + Condition::LessEqual => dynasm!(self ; setle Rb(dst as u8)), + Condition::Equal => dynasm!(self ; sete Rb(dst as u8)), + Condition::NotEqual => dynasm!(self ; setne Rb(dst as u8)), + Condition::Signed => dynasm!(self ; sets Rb(dst as u8)), + _ => unreachable!(), + } + } + fn emit_push(&mut self, sz: Size, src: Location) { + match (sz, src) { + (Size::S64, Location::Imm32(src)) => dynasm!(self ; push src as i32), + (Size::S64, Location::GPR(src)) => dynasm!(self ; push Rq(src as u8)), + (Size::S64, Location::Memory(src, disp)) => { + dynasm!(self ; push QWORD [Rq(src as u8) + disp]) + } + _ => panic!("push {:?} {:?}", sz, src), + } + } + fn emit_pop(&mut self, sz: Size, dst: Location) { + match (sz, dst) { + (Size::S64, Location::GPR(dst)) => dynasm!(self ; pop Rq(dst as u8)), + (Size::S64, Location::Memory(dst, disp)) => { + dynasm!(self ; pop QWORD [Rq(dst as u8) + disp]) + } + _ => panic!("pop {:?} {:?}", sz, dst), + } + } + fn emit_cmp(&mut self, sz: Size, left: Location, right: Location) { + binop_all_nofp!(cmp, self, sz, left, right, { + panic!("{:?} {:?} {:?}", sz, left, right); + }); + } + fn emit_add(&mut self, sz: Size, src: Location, dst: Location) { + binop_all_nofp!(add, self, sz, src, dst, { unreachable!() }); + } + fn emit_sub(&mut self, sz: Size, src: Location, dst: Location) { + binop_all_nofp!(sub, self, sz, src, dst, { unreachable!() }); + } + fn emit_imul(&mut self, sz: Size, src: Location, dst: Location) { + binop_gpr_gpr!(imul, self, sz, src, dst, { + binop_mem_gpr!(imul, self, sz, src, dst, { unreachable!() }) + }); + } + fn emit_imul_imm32_gpr64(&mut self, src: u32, dst: GPR) { + dynasm!(self ; imul Rq(dst as u8), Rq(dst as u8), src as i32); + } + fn emit_div(&mut self, sz: Size, divisor: Location) { + unop_gpr_or_mem!(div, self, sz, divisor, { unreachable!() }); + } + fn emit_idiv(&mut self, sz: Size, divisor: Location) { + unop_gpr_or_mem!(idiv, self, sz, divisor, { unreachable!() }); + } + fn emit_shl(&mut self, sz: Size, src: Location, dst: Location) { + binop_shift!(shl, self, sz, src, dst, { unreachable!() }); + } + fn emit_shr(&mut self, sz: Size, src: Location, dst: Location) { + binop_shift!(shr, self, sz, src, dst, { unreachable!() }); + } + fn emit_sar(&mut self, sz: Size, src: Location, dst: Location) { + binop_shift!(sar, self, sz, src, dst, { unreachable!() }); + } + fn emit_rol(&mut self, sz: Size, src: Location, dst: Location) { + binop_shift!(rol, self, sz, src, dst, { unreachable!() }); + } + fn emit_ror(&mut self, sz: Size, src: Location, dst: Location) { + binop_shift!(ror, self, sz, src, dst, { unreachable!() }); + } + fn emit_and(&mut self, sz: Size, src: Location, dst: Location) { + binop_all_nofp!(and, self, sz, src, dst, { unreachable!() }); + } + fn emit_or(&mut self, sz: Size, src: Location, dst: Location) { + binop_all_nofp!(or, self, sz, src, dst, { unreachable!() }); + } + fn emit_lzcnt(&mut self, sz: Size, src: Location, dst: Location) { + binop_gpr_gpr!(lzcnt, self, sz, src, dst, { + binop_mem_gpr!(lzcnt, self, sz, src, dst, { unreachable!() }) + }); + } + fn emit_tzcnt(&mut self, sz: Size, src: Location, dst: Location) { + binop_gpr_gpr!(tzcnt, self, sz, src, dst, { + binop_mem_gpr!(tzcnt, self, sz, src, dst, { unreachable!() }) + }); + } + fn emit_popcnt(&mut self, sz: Size, src: Location, dst: Location) { + binop_gpr_gpr!(popcnt, self, sz, src, dst, { + binop_mem_gpr!(popcnt, self, sz, src, dst, { unreachable!() }) + }); + } + fn emit_movzx(&mut self, sz_src: Size, src: Location, sz_dst: Size, dst: Location) { + match (sz_src, src, sz_dst, dst) { + (Size::S8, Location::GPR(src), Size::S32, Location::GPR(dst)) => { + dynasm!(self ; movzx Rd(dst as u8), Rb(src as u8)); + } + (Size::S16, Location::GPR(src), Size::S32, Location::GPR(dst)) => { + dynasm!(self ; movzx Rd(dst as u8), Rw(src as u8)); + } + (Size::S8, Location::Memory(src, disp), Size::S32, Location::GPR(dst)) => { + dynasm!(self ; movzx Rd(dst as u8), BYTE [Rq(src as u8) + disp]); + } + (Size::S16, Location::Memory(src, disp), Size::S32, Location::GPR(dst)) => { + dynasm!(self ; movzx Rd(dst as u8), WORD [Rq(src as u8) + disp]); + } + (Size::S8, Location::GPR(src), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movzx Rq(dst as u8), Rb(src as u8)); + } + (Size::S16, Location::GPR(src), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movzx Rq(dst as u8), Rw(src as u8)); + } + (Size::S8, Location::Memory(src, disp), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movzx Rq(dst as u8), BYTE [Rq(src as u8) + disp]); + } + (Size::S16, Location::Memory(src, disp), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movzx Rq(dst as u8), WORD [Rq(src as u8) + disp]); + } + _ => unreachable!(), + } + } + fn emit_movsx(&mut self, sz_src: Size, src: Location, sz_dst: Size, dst: Location) { + match (sz_src, src, sz_dst, dst) { + (Size::S8, Location::GPR(src), Size::S32, Location::GPR(dst)) => { + dynasm!(self ; movsx Rd(dst as u8), Rb(src as u8)); + } + (Size::S16, Location::GPR(src), Size::S32, Location::GPR(dst)) => { + dynasm!(self ; movsx Rd(dst as u8), Rw(src as u8)); + } + (Size::S8, Location::Memory(src, disp), Size::S32, Location::GPR(dst)) => { + dynasm!(self ; movsx Rd(dst as u8), BYTE [Rq(src as u8) + disp]); + } + (Size::S16, Location::Memory(src, disp), Size::S32, Location::GPR(dst)) => { + dynasm!(self ; movsx Rd(dst as u8), WORD [Rq(src as u8) + disp]); + } + (Size::S8, Location::GPR(src), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movsx Rq(dst as u8), Rb(src as u8)); + } + (Size::S16, Location::GPR(src), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movsx Rq(dst as u8), Rw(src as u8)); + } + (Size::S32, Location::GPR(src), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movsx Rq(dst as u8), Rd(src as u8)); + } + (Size::S8, Location::Memory(src, disp), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movsx Rq(dst as u8), BYTE [Rq(src as u8) + disp]); + } + (Size::S16, Location::Memory(src, disp), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movsx Rq(dst as u8), WORD [Rq(src as u8) + disp]); + } + (Size::S32, Location::Memory(src, disp), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movsx Rq(dst as u8), DWORD [Rq(src as u8) + disp]); + } + _ => unreachable!(), + } + } + + fn emit_btc_gpr_imm8_32(&mut self, src: u8, dst: GPR) { + dynasm!(self ; btc Rd(dst as u8), BYTE src as i8); + } + + fn emit_btc_gpr_imm8_64(&mut self, src: u8, dst: GPR) { + dynasm!(self ; btc Rq(dst as u8), BYTE src as i8); + } + + fn emit_cmovae_gpr_32(&mut self, src: GPR, dst: GPR) { + dynasm!(self ; cmovae Rd(dst as u8), Rd(src as u8)); + } + + fn emit_cmovae_gpr_64(&mut self, src: GPR, dst: GPR) { + dynasm!(self ; cmovae Rq(dst as u8), Rq(src as u8)); + } + + avx_fn!(vaddss, emit_vaddss); + avx_fn!(vaddsd, emit_vaddsd); + + avx_fn!(vsubss, emit_vsubss); + avx_fn!(vsubsd, emit_vsubsd); + + avx_fn!(vmulss, emit_vmulss); + avx_fn!(vmulsd, emit_vmulsd); + + avx_fn!(vdivss, emit_vdivss); + avx_fn!(vdivsd, emit_vdivsd); + + avx_fn!(vmaxss, emit_vmaxss); + avx_fn!(vmaxsd, emit_vmaxsd); + + avx_fn!(vminss, emit_vminss); + avx_fn!(vminsd, emit_vminsd); + + avx_fn!(vcmpeqss, emit_vcmpeqss); + avx_fn!(vcmpeqsd, emit_vcmpeqsd); + + avx_fn!(vcmpneqss, emit_vcmpneqss); + avx_fn!(vcmpneqsd, emit_vcmpneqsd); + + avx_fn!(vcmpltss, emit_vcmpltss); + avx_fn!(vcmpltsd, emit_vcmpltsd); + + avx_fn!(vcmpless, emit_vcmpless); + avx_fn!(vcmplesd, emit_vcmplesd); + + avx_fn!(vcmpgtss, emit_vcmpgtss); + avx_fn!(vcmpgtsd, emit_vcmpgtsd); + + avx_fn!(vcmpgess, emit_vcmpgess); + avx_fn!(vcmpgesd, emit_vcmpgesd); + + avx_fn!(vsqrtss, emit_vsqrtss); + avx_fn!(vsqrtsd, emit_vsqrtsd); + + avx_fn!(vcvtss2sd, emit_vcvtss2sd); + avx_fn!(vcvtsd2ss, emit_vcvtsd2ss); + + avx_round_fn!(vroundss, emit_vroundss_nearest, 0); + avx_round_fn!(vroundss, emit_vroundss_floor, 1); + avx_round_fn!(vroundss, emit_vroundss_ceil, 2); + avx_round_fn!(vroundss, emit_vroundss_trunc, 3); + avx_round_fn!(vroundsd, emit_vroundsd_nearest, 0); + avx_round_fn!(vroundsd, emit_vroundsd_floor, 1); + avx_round_fn!(vroundsd, emit_vroundsd_ceil, 2); + avx_round_fn!(vroundsd, emit_vroundsd_trunc, 3); + + avx_i2f_32_fn!(vcvtsi2ss, emit_vcvtsi2ss_32); + avx_i2f_32_fn!(vcvtsi2sd, emit_vcvtsi2sd_32); + avx_i2f_64_fn!(vcvtsi2ss, emit_vcvtsi2ss_64); + avx_i2f_64_fn!(vcvtsi2sd, emit_vcvtsi2sd_64); + + fn emit_ucomiss(&mut self, src: XMMOrMemory, dst: XMM) { + match src { + XMMOrMemory::XMM(x) => dynasm!(self ; ucomiss Rx(dst as u8), Rx(x as u8)), + XMMOrMemory::Memory(base, disp) => { + dynasm!(self ; ucomiss Rx(dst as u8), [Rq(base as u8) + disp]) + } + } + } + + fn emit_ucomisd(&mut self, src: XMMOrMemory, dst: XMM) { + match src { + XMMOrMemory::XMM(x) => dynasm!(self ; ucomisd Rx(dst as u8), Rx(x as u8)), + XMMOrMemory::Memory(base, disp) => { + dynasm!(self ; ucomisd Rx(dst as u8), [Rq(base as u8) + disp]) + } + } + } + + fn emit_cvttss2si_32(&mut self, src: XMMOrMemory, dst: GPR) { + match src { + XMMOrMemory::XMM(x) => dynasm!(self ; cvttss2si Rd(dst as u8), Rx(x as u8)), + XMMOrMemory::Memory(base, disp) => { + dynasm!(self ; cvttss2si Rd(dst as u8), [Rq(base as u8) + disp]) + } + } + } + + fn emit_cvttss2si_64(&mut self, src: XMMOrMemory, dst: GPR) { + match src { + XMMOrMemory::XMM(x) => dynasm!(self ; cvttss2si Rq(dst as u8), Rx(x as u8)), + XMMOrMemory::Memory(base, disp) => { + dynasm!(self ; cvttss2si Rq(dst as u8), [Rq(base as u8) + disp]) + } + } + } + + fn emit_cvttsd2si_32(&mut self, src: XMMOrMemory, dst: GPR) { + match src { + XMMOrMemory::XMM(x) => dynasm!(self ; cvttsd2si Rd(dst as u8), Rx(x as u8)), + XMMOrMemory::Memory(base, disp) => { + dynasm!(self ; cvttsd2si Rd(dst as u8), [Rq(base as u8) + disp]) + } + } + } + + fn emit_cvttsd2si_64(&mut self, src: XMMOrMemory, dst: GPR) { + match src { + XMMOrMemory::XMM(x) => dynasm!(self ; cvttsd2si Rq(dst as u8), Rx(x as u8)), + XMMOrMemory::Memory(base, disp) => { + dynasm!(self ; cvttsd2si Rq(dst as u8), [Rq(base as u8) + disp]) + } + } + } + + fn emit_test_gpr_64(&mut self, reg: GPR) { + dynasm!(self ; test Rq(reg as u8), Rq(reg as u8)); + } + + fn emit_ud2(&mut self) { + dynasm!(self ; ud2); + } + fn emit_ret(&mut self) { + dynasm!(self ; ret); + } + + fn emit_call_label(&mut self, label: Self::Label) { + dynasm!(self ; call =>label); + } + fn emit_call_location(&mut self, loc: Location) { + match loc { + Location::GPR(x) => dynasm!(self ; call Rq(x as u8)), + Location::Memory(base, disp) => dynasm!(self ; call QWORD [Rq(base as u8) + disp]), + _ => unreachable!(), + } + } + + fn emit_bkpt(&mut self) { + dynasm!(self ; int 0x3); + } +} diff --git a/lib/singlepass-backend/src/lib.rs b/lib/singlepass-backend/src/lib.rs new file mode 100644 index 000000000..f1011591d --- /dev/null +++ b/lib/singlepass-backend/src/lib.rs @@ -0,0 +1,36 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] +#![feature(proc_macro_hygiene)] + +#[cfg(not(any( + all(target_os = "macos", target_arch = "x86_64"), + all(target_os = "linux", target_arch = "x86_64"), +)))] +compile_error!("This crate doesn't yet support compiling on operating systems other than linux and macos and architectures other than x86_64"); + +extern crate dynasmrt; + +#[macro_use] +extern crate dynasm; + +#[macro_use] +extern crate lazy_static; + +extern crate byteorder; +#[macro_use] +extern crate smallvec; + +mod codegen_x64; +mod emitter_x64; +mod machine; +mod protect_unix; + +pub use codegen_x64::X64FunctionCode as FunctionCodeGenerator; +pub use codegen_x64::X64ModuleCodeGenerator as ModuleCodeGenerator; + +use wasmer_runtime_core::codegen::SimpleStreamingCompilerGen; +pub type SinglePassCompiler = SimpleStreamingCompilerGen< + codegen_x64::X64ModuleCodeGenerator, + codegen_x64::X64FunctionCode, + codegen_x64::X64ExecutionContext, + codegen_x64::CodegenError, +>; diff --git a/lib/singlepass-backend/src/machine.rs b/lib/singlepass-backend/src/machine.rs new file mode 100644 index 000000000..bd2e43fc0 --- /dev/null +++ b/lib/singlepass-backend/src/machine.rs @@ -0,0 +1,419 @@ +use crate::emitter_x64::*; +use smallvec::SmallVec; +use std::collections::HashSet; +use wasmparser::Type as WpType; + +struct MachineStackOffset(usize); + +pub struct Machine { + used_gprs: HashSet, + used_xmms: HashSet, + stack_offset: MachineStackOffset, + save_area_offset: Option, +} + +impl Machine { + pub fn new() -> Self { + Machine { + used_gprs: HashSet::new(), + used_xmms: HashSet::new(), + stack_offset: MachineStackOffset(0), + save_area_offset: None, + } + } + + pub fn get_stack_offset(&self) -> usize { + self.stack_offset.0 + } + + pub fn get_used_gprs(&self) -> Vec { + self.used_gprs.iter().cloned().collect() + } + + pub fn get_used_xmms(&self) -> Vec { + self.used_xmms.iter().cloned().collect() + } + + pub fn get_vmctx_reg() -> GPR { + GPR::R15 + } + + /// Picks an unused general purpose register for local/stack/argument use. + /// + /// This method does not mark the register as used. + pub fn pick_gpr(&self) -> Option { + use GPR::*; + static REGS: &'static [GPR] = &[RSI, RDI, R8, R9, R10, R11]; + for r in REGS { + if !self.used_gprs.contains(r) { + return Some(*r); + } + } + None + } + + /// Picks an unused general purpose register for internal temporary use. + /// + /// This method does not mark the register as used. + pub fn pick_temp_gpr(&self) -> Option { + use GPR::*; + static REGS: &'static [GPR] = &[RAX, RCX, RDX]; + for r in REGS { + if !self.used_gprs.contains(r) { + return Some(*r); + } + } + None + } + + /// Acquires a temporary GPR. + pub fn acquire_temp_gpr(&mut self) -> Option { + let gpr = self.pick_temp_gpr(); + if let Some(x) = gpr { + self.used_gprs.insert(x); + } + gpr + } + + /// Releases a temporary GPR. + pub fn release_temp_gpr(&mut self, gpr: GPR) { + assert_eq!(self.used_gprs.remove(&gpr), true); + } + + /// Picks an unused XMM register. + /// + /// This method does not mark the register as used. + pub fn pick_xmm(&self) -> Option { + use XMM::*; + static REGS: &'static [XMM] = &[XMM3, XMM4, XMM5, XMM6, XMM7]; + for r in REGS { + if !self.used_xmms.contains(r) { + return Some(*r); + } + } + None + } + + /// Picks an unused XMM register for internal temporary use. + /// + /// This method does not mark the register as used. + pub fn pick_temp_xmm(&self) -> Option { + use XMM::*; + static REGS: &'static [XMM] = &[XMM0, XMM1, XMM2]; + for r in REGS { + if !self.used_xmms.contains(r) { + return Some(*r); + } + } + None + } + + /// Acquires a temporary XMM register. + pub fn acquire_temp_xmm(&mut self) -> Option { + let xmm = self.pick_temp_xmm(); + if let Some(x) = xmm { + self.used_xmms.insert(x); + } + xmm + } + + /// Releases a temporary XMM register. + pub fn release_temp_xmm(&mut self, xmm: XMM) { + assert_eq!(self.used_xmms.remove(&xmm), true); + } + + /// Acquires locations from the machine state. + /// + /// If the returned locations are used for stack value, `release_location` needs to be called on them; + /// Otherwise, if the returned locations are used for locals, `release_location` does not need to be called on them. + pub fn acquire_locations( + &mut self, + assembler: &mut E, + tys: &[WpType], + zeroed: bool, + ) -> SmallVec<[Location; 1]> { + let mut ret = smallvec![]; + let mut delta_stack_offset: usize = 0; + + for ty in tys { + let loc = match *ty { + WpType::F32 | WpType::F64 => self.pick_xmm().map(Location::XMM), + WpType::I32 | WpType::I64 => self.pick_gpr().map(Location::GPR), + _ => unreachable!(), + }; + + let loc = if let Some(x) = loc { + x + } else { + self.stack_offset.0 += 8; + delta_stack_offset += 8; + Location::Memory(GPR::RBP, -(self.stack_offset.0 as i32)) + }; + if let Location::GPR(x) = loc { + self.used_gprs.insert(x); + } else if let Location::XMM(x) = loc { + self.used_xmms.insert(x); + } + ret.push(loc); + } + + if delta_stack_offset != 0 { + assembler.emit_sub( + Size::S64, + Location::Imm32(delta_stack_offset as u32), + Location::GPR(GPR::RSP), + ); + } + if zeroed { + for i in 0..tys.len() { + assembler.emit_mov(Size::S64, Location::Imm32(0), ret[i]); + } + } + ret + } + + /// Releases locations used for stack value. + pub fn release_locations(&mut self, assembler: &mut E, locs: &[Location]) { + let mut delta_stack_offset: usize = 0; + + for loc in locs.iter().rev() { + match *loc { + Location::GPR(ref x) => { + assert_eq!(self.used_gprs.remove(x), true); + } + Location::XMM(ref x) => { + assert_eq!(self.used_xmms.remove(x), true); + } + Location::Memory(GPR::RBP, x) => { + if x >= 0 { + unreachable!(); + } + let offset = (-x) as usize; + if offset != self.stack_offset.0 { + unreachable!(); + } + self.stack_offset.0 -= 8; + delta_stack_offset += 8; + } + _ => {} + } + } + + if delta_stack_offset != 0 { + assembler.emit_add( + Size::S64, + Location::Imm32(delta_stack_offset as u32), + Location::GPR(GPR::RSP), + ); + } + } + + pub fn release_locations_only_regs(&mut self, locs: &[Location]) { + for loc in locs.iter().rev() { + match *loc { + Location::GPR(ref x) => { + assert_eq!(self.used_gprs.remove(x), true); + } + Location::XMM(ref x) => { + assert_eq!(self.used_xmms.remove(x), true); + } + _ => {} + } + } + } + + pub fn release_locations_only_stack( + &mut self, + assembler: &mut E, + locs: &[Location], + ) { + let mut delta_stack_offset: usize = 0; + + for loc in locs.iter().rev() { + match *loc { + Location::Memory(GPR::RBP, x) => { + if x >= 0 { + unreachable!(); + } + let offset = (-x) as usize; + if offset != self.stack_offset.0 { + unreachable!(); + } + self.stack_offset.0 -= 8; + delta_stack_offset += 8; + } + _ => {} + } + } + + if delta_stack_offset != 0 { + assembler.emit_add( + Size::S64, + Location::Imm32(delta_stack_offset as u32), + Location::GPR(GPR::RSP), + ); + } + } + + pub fn release_locations_keep_state(&self, assembler: &mut E, locs: &[Location]) { + let mut delta_stack_offset: usize = 0; + + for loc in locs.iter().rev() { + match *loc { + Location::Memory(GPR::RBP, x) => { + if x >= 0 { + unreachable!(); + } + let offset = (-x) as usize; + if offset != self.stack_offset.0 { + unreachable!(); + } + delta_stack_offset += 8; + } + _ => {} + } + } + + if delta_stack_offset != 0 { + assembler.emit_add( + Size::S64, + Location::Imm32(delta_stack_offset as u32), + Location::GPR(GPR::RSP), + ); + } + } + + pub fn init_locals( + &mut self, + a: &mut E, + n: usize, + n_params: usize, + ) -> Vec { + // Use callee-saved registers for locals. + fn get_local_location(idx: usize) -> Location { + match idx { + 0 => Location::GPR(GPR::R12), + 1 => Location::GPR(GPR::R13), + 2 => Location::GPR(GPR::R14), + 3 => Location::GPR(GPR::RBX), + _ => Location::Memory(GPR::RBP, -(((idx - 3) * 8) as i32)), + } + } + + let mut locations: Vec = vec![]; + let mut allocated: usize = 0; + + // Determine locations for parameters. + for i in 0..n_params { + let loc = Self::get_param_location(i + 1); + locations.push(match loc { + Location::GPR(_) => { + let old_idx = allocated; + allocated += 1; + get_local_location(old_idx) + } + Location::Memory(_, _) => loc, + _ => unreachable!(), + }); + } + + // Determine locations for normal locals. + for _ in n_params..n { + locations.push(get_local_location(allocated)); + allocated += 1; + } + + // How many machine stack slots did all the locals use? + let num_mem_slots = locations + .iter() + .filter(|&&loc| match loc { + Location::Memory(_, _) => true, + _ => false, + }) + .count(); + + // Move RSP down to reserve space for machine stack slots. + if num_mem_slots > 0 { + a.emit_sub( + Size::S64, + Location::Imm32((num_mem_slots * 8) as u32), + Location::GPR(GPR::RSP), + ); + self.stack_offset.0 += num_mem_slots * 8; + } + + // Save callee-saved registers. + for loc in locations.iter() { + if let Location::GPR(_) = *loc { + a.emit_push(Size::S64, *loc); + self.stack_offset.0 += 8; + } + } + + // Save R15 for vmctx use. + a.emit_push(Size::S64, Location::GPR(GPR::R15)); + self.stack_offset.0 += 8; + + // Save the offset of static area. + self.save_area_offset = Some(MachineStackOffset(self.stack_offset.0)); + + // Load in-register parameters into the allocated locations. + for i in 0..n_params { + let loc = Self::get_param_location(i + 1); + match loc { + Location::GPR(_) => { + a.emit_mov(Size::S64, loc, locations[i]); + } + _ => break, + } + } + + // Load vmctx. + a.emit_mov( + Size::S64, + Self::get_param_location(0), + Location::GPR(GPR::R15), + ); + + // Initialize all normal locals to zero. + for i in n_params..n { + a.emit_mov(Size::S64, Location::Imm32(0), locations[i]); + } + + locations + } + + pub fn finalize_locals(&mut self, a: &mut E, locations: &[Location]) { + // Unwind stack to the "save area". + a.emit_lea( + Size::S64, + Location::Memory( + GPR::RBP, + -(self.save_area_offset.as_ref().unwrap().0 as i32), + ), + Location::GPR(GPR::RSP), + ); + + // Restore R15 used by vmctx. + a.emit_pop(Size::S64, Location::GPR(GPR::R15)); + + // Restore callee-saved registers. + for loc in locations.iter().rev() { + if let Location::GPR(_) = *loc { + a.emit_pop(Size::S64, *loc); + } + } + } + + pub fn get_param_location(idx: usize) -> Location { + match idx { + 0 => Location::GPR(GPR::RDI), + 1 => Location::GPR(GPR::RSI), + 2 => Location::GPR(GPR::RDX), + 3 => Location::GPR(GPR::RCX), + 4 => Location::GPR(GPR::R8), + 5 => Location::GPR(GPR::R9), + _ => Location::Memory(GPR::RBP, (16 + (idx - 6) * 8) as i32), + } + } +} diff --git a/lib/singlepass-backend/src/protect_unix.rs b/lib/singlepass-backend/src/protect_unix.rs new file mode 100644 index 000000000..83ba42c79 --- /dev/null +++ b/lib/singlepass-backend/src/protect_unix.rs @@ -0,0 +1,234 @@ +//! Installing signal handlers allows us to handle traps and out-of-bounds memory +//! accesses that occur when runniing webassembly. +//! +//! This code is inspired by: https://github.com/pepyakin/wasmtime/commit/625a2b6c0815b21996e111da51b9664feb174622 +//! +//! When a WebAssembly module triggers any traps, we perform recovery here. +//! +//! This module uses TLS (thread-local storage) to track recovery information. Since the four signals we're handling +//! are very special, the async signal unsafety of Rust's TLS implementation generally does not affect the correctness here +//! unless you have memory unsafety elsewhere in your code. +//! +use libc::{c_int, c_void, siginfo_t}; +use nix::sys::signal::{ + sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV, + SIGTRAP, +}; +use std::any::Any; +use std::cell::{Cell, RefCell, UnsafeCell}; +use std::collections::HashMap; +use std::ptr; +use std::sync::Arc; +use std::sync::Once; +use wasmer_runtime_core::codegen::BkptInfo; +use wasmer_runtime_core::typed_func::WasmTrapInfo; + +extern "C" fn signal_trap_handler( + signum: ::nix::libc::c_int, + siginfo: *mut siginfo_t, + ucontext: *mut c_void, +) { + unsafe { + match Signal::from_c_int(signum) { + Ok(SIGTRAP) => { + let (_, ip) = get_faulting_addr_and_ip(siginfo as _, ucontext); + let bkpt_map = BKPT_MAP.with(|x| x.borrow().last().map(|x| x.clone())); + if let Some(bkpt_map) = bkpt_map { + if let Some(ref x) = bkpt_map.get(&(ip as usize)) { + (x)(BkptInfo {}); + return; + } + } + } + _ => {} + } + + do_unwind(signum, siginfo as _, ucontext); + } +} + +extern "C" { + pub fn setjmp(env: *mut c_void) -> c_int; + fn longjmp(env: *mut c_void, val: c_int) -> !; +} + +pub unsafe fn install_sighandler() { + let sa = SigAction::new( + SigHandler::SigAction(signal_trap_handler), + SaFlags::SA_ONSTACK, + SigSet::empty(), + ); + sigaction(SIGFPE, &sa).unwrap(); + sigaction(SIGILL, &sa).unwrap(); + sigaction(SIGSEGV, &sa).unwrap(); + sigaction(SIGBUS, &sa).unwrap(); + sigaction(SIGTRAP, &sa).unwrap(); +} + +const SETJMP_BUFFER_LEN: usize = 27; +pub static SIGHANDLER_INIT: Once = Once::new(); + +thread_local! { + pub static SETJMP_BUFFER: UnsafeCell<[c_int; SETJMP_BUFFER_LEN]> = UnsafeCell::new([0; SETJMP_BUFFER_LEN]); + pub static CAUGHT_ADDRESSES: Cell<(*const c_void, *const c_void)> = Cell::new((ptr::null(), ptr::null())); + pub static CURRENT_EXECUTABLE_BUFFER: Cell<*const c_void> = Cell::new(ptr::null()); + pub static TRAP_EARLY_DATA: Cell>> = Cell::new(None); + pub static BKPT_MAP: RefCell>>>> = RefCell::new(Vec::new()); +} + +pub unsafe fn trigger_trap() -> ! { + let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get()); + + longjmp(jmp_buf as *mut c_void, 0) +} + +pub enum CallProtError { + Trap(WasmTrapInfo), + Error(Box), +} + +pub fn call_protected(f: impl FnOnce() -> T) -> Result { + unsafe { + let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get()); + let prev_jmp_buf = *jmp_buf; + + SIGHANDLER_INIT.call_once(|| { + install_sighandler(); + }); + + let signum = setjmp(jmp_buf as *mut _); + if signum != 0 { + *jmp_buf = prev_jmp_buf; + + if let Some(data) = TRAP_EARLY_DATA.with(|cell| cell.replace(None)) { + Err(CallProtError::Error(data)) + } else { + // let (faulting_addr, _inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get()); + + // let signal = match Signal::from_c_int(signum) { + // Ok(SIGFPE) => "floating-point exception", + // Ok(SIGILL) => "illegal instruction", + // Ok(SIGSEGV) => "segmentation violation", + // Ok(SIGBUS) => "bus error", + // Err(_) => "error while getting the Signal", + // _ => "unknown trapped signal", + // }; + // // When the trap-handler is fully implemented, this will return more information. + // Err(RuntimeError::Trap { + // msg: format!("unknown trap at {:p} - {}", faulting_addr, signal).into(), + // } + // .into()) + Err(CallProtError::Trap(WasmTrapInfo::Unknown)) + } + } else { + let ret = f(); // TODO: Switch stack? + *jmp_buf = prev_jmp_buf; + Ok(ret) + } + } +} + +/// Unwinds to last protected_call. +pub unsafe fn do_unwind(signum: i32, siginfo: *const c_void, ucontext: *const c_void) -> ! { + // Since do_unwind is only expected to get called from WebAssembly code which doesn't hold any host resources (locks etc.) + // itself, accessing TLS here is safe. In case any other code calls this, it often indicates a memory safety bug and you should + // temporarily disable the signal handlers to debug it. + + let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get()); + if *jmp_buf == [0; SETJMP_BUFFER_LEN] { + ::std::process::abort(); + } + + CAUGHT_ADDRESSES.with(|cell| cell.set(get_faulting_addr_and_ip(siginfo, ucontext))); + + longjmp(jmp_buf as *mut ::nix::libc::c_void, signum) +} + +#[cfg(all(target_os = "linux", target_arch = "x86_64"))] +unsafe fn get_faulting_addr_and_ip( + siginfo: *const c_void, + ucontext: *const c_void, +) -> (*const c_void, *const c_void) { + use libc::{ucontext_t, RIP}; + + #[allow(dead_code)] + #[repr(C)] + struct siginfo_t { + si_signo: i32, + si_errno: i32, + si_code: i32, + si_addr: u64, + // ... + } + + let siginfo = siginfo as *const siginfo_t; + let si_addr = (*siginfo).si_addr; + + let ucontext = ucontext as *const ucontext_t; + let rip = (*ucontext).uc_mcontext.gregs[RIP as usize]; + + (si_addr as _, rip as _) +} + +#[cfg(all(target_os = "macos", target_arch = "x86_64"))] +unsafe fn get_faulting_addr_and_ip( + siginfo: *const c_void, + ucontext: *const c_void, +) -> (*const c_void, *const c_void) { + #[allow(dead_code)] + #[repr(C)] + struct ucontext_t { + uc_onstack: u32, + uc_sigmask: u32, + uc_stack: libc::stack_t, + uc_link: *const ucontext_t, + uc_mcsize: u64, + uc_mcontext: *const mcontext_t, + } + #[repr(C)] + struct exception_state { + trapno: u16, + cpu: u16, + err: u32, + faultvaddr: u64, + } + #[repr(C)] + struct regs { + rax: u64, + rbx: u64, + rcx: u64, + rdx: u64, + rdi: u64, + rsi: u64, + rbp: u64, + rsp: u64, + r8: u64, + r9: u64, + r10: u64, + r11: u64, + r12: u64, + r13: u64, + r14: u64, + r15: u64, + rip: u64, + rflags: u64, + cs: u64, + fs: u64, + gs: u64, + } + #[allow(dead_code)] + #[repr(C)] + struct mcontext_t { + es: exception_state, + ss: regs, + // ... + } + + let siginfo = siginfo as *const siginfo_t; + let si_addr = (*siginfo).si_addr; + + let ucontext = ucontext as *const ucontext_t; + let rip = (*(*ucontext).uc_mcontext).ss.rip; + + (si_addr, rip as _) +} diff --git a/lib/spectests/Cargo.toml b/lib/spectests/Cargo.toml index fea4560c5..88136daa1 100644 --- a/lib/spectests/Cargo.toml +++ b/lib/spectests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-spectests" -version = "0.2.0" +version = "0.4.1" description = "Wasmer spectests library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,20 +9,20 @@ edition = "2018" build = "build/mod.rs" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.2.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } +wasmer-clif-backend = { path = "../clif-backend", version = "0.4.1" } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.4.1", optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.1", optional = true } [build-dependencies] wabt = "0.7.2" [dev-dependencies] -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"] fast-tests = [] clif = [] -llvm = [] \ No newline at end of file +llvm = ["wasmer-llvm-backend"] +singlepass = ["wasmer-singlepass-backend"] diff --git a/lib/spectests/README.md b/lib/spectests/README.md new file mode 100644 index 000000000..8f9cee65b --- /dev/null +++ b/lib/spectests/README.md @@ -0,0 +1,32 @@ +

+ + Wasmer logo + +

+ +

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

+ +# Wasmer Spectests + +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 allows to test the Wasmer runtime against the official +specification test suite. diff --git a/lib/spectests/build/spectests.rs b/lib/spectests/build/spectests.rs index c1d28ca29..10fb45678 100644 --- a/lib/spectests/build/spectests.rs +++ b/lib/spectests/build/spectests.rs @@ -107,7 +107,13 @@ fn get_compiler() -> impl Compiler { LLVMCompiler::new() } -#[cfg(not(any(feature = "llvm", feature = "clif")))] +#[cfg(feature = "singlepass")] +fn get_compiler() -> impl Compiler { + use wasmer_singlepass_backend::SinglePassCompiler; + SinglePassCompiler::new() +} + +#[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))] fn get_compiler() -> impl Compiler { panic!("compiler not specified, activate a compiler via features"); use wasmer_clif_backend::CraneliftCompiler; diff --git a/lib/spectests/examples/simple/main.rs b/lib/spectests/examples/simple/main.rs index 595bb3680..357adb5f7 100644 --- a/lib/spectests/examples/simple/main.rs +++ b/lib/spectests/examples/simple/main.rs @@ -22,7 +22,13 @@ fn get_compiler() -> impl Compiler { LLVMCompiler::new() } -#[cfg(not(any(feature = "llvm", feature = "clif")))] +#[cfg(feature = "singlepass")] +fn get_compiler() -> impl Compiler { + use wasmer_singlepass_backend::SinglePassCompiler; + SinglePassCompiler::new() +} + +#[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))] fn get_compiler() -> impl Compiler { panic!("compiler not specified, activate a compiler via features"); use wasmer_clif_backend::CraneliftCompiler; diff --git a/lib/wasi/Cargo.toml b/lib/wasi/Cargo.toml new file mode 100644 index 000000000..c4c936a02 --- /dev/null +++ b/lib/wasi/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "wasmer-wasi" +version = "0.4.1" +description = "Wasmer runtime WASI implementation library" +license = "MIT" +authors = ["The Wasmer Engineering Team "] +repository = "https://github.com/wasmerio/wasmer" +edition = "2018" + +[dependencies] +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } +libc = "0.2.50" +rand = "0.6.5" +# wasmer-runtime-abi = { path = "../runtime-abi" } +hashbrown = "0.1.8" +generational-arena = "0.2.2" +log = "0.4.6" +byteorder = "1.3.1" diff --git a/lib/wasi/src/lib.rs b/lib/wasi/src/lib.rs new file mode 100644 index 000000000..86f9ceaac --- /dev/null +++ b/lib/wasi/src/lib.rs @@ -0,0 +1,102 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + +#[macro_use] +extern crate log; + +#[macro_use] +mod macros; +mod ptr; +mod state; +mod syscalls; +mod utils; + +use self::state::{WasiFs, WasiState}; +use self::syscalls::*; + +use std::ffi::c_void; + +pub use self::utils::is_wasi_module; + +use wasmer_runtime_core::{func, import::ImportObject, imports}; + +/// This is returned in the Box RuntimeError::Error variant. +/// Use `downcast` or `downcast_ref` to retrieve the `ExitCode`. +pub struct ExitCode { + pub code: syscalls::types::__wasi_exitcode_t, +} + +pub fn generate_import_object( + args: Vec>, + envs: Vec>, + preopened_files: Vec, +) -> ImportObject { + let state_gen = move || { + fn state_destructor(data: *mut c_void) { + unsafe { + drop(Box::from_raw(data as *mut WasiState)); + } + } + + let state = Box::new(WasiState { + fs: WasiFs::new(&preopened_files).unwrap(), + args: &args[..], + envs: &envs[..], + }); + + ( + Box::leak(state) as *mut WasiState as *mut c_void, + state_destructor as fn(*mut c_void), + ) + }; + imports! { + // This generates the wasi state. + state_gen, + "wasi_unstable" => { + "args_get" => func!(args_get), + "args_sizes_get" => func!(args_sizes_get), + "clock_res_get" => func!(clock_res_get), + "clock_time_get" => func!(clock_time_get), + "environ_get" => func!(environ_get), + "environ_sizes_get" => func!(environ_sizes_get), + "fd_advise" => func!(fd_advise), + "fd_allocate" => func!(fd_allocate), + "fd_close" => func!(fd_close), + "fd_datasync" => func!(fd_datasync), + "fd_fdstat_get" => func!(fd_fdstat_get), + "fd_fdstat_set_flags" => func!(fd_fdstat_set_flags), + "fd_fdstat_set_rights" => func!(fd_fdstat_set_rights), + "fd_filestat_get" => func!(fd_filestat_get), + "fd_filestat_set_size" => func!(fd_filestat_set_size), + "fd_filestat_set_times" => func!(fd_filestat_set_times), + "fd_pread" => func!(fd_pread), + "fd_prestat_get" => func!(fd_prestat_get), + "fd_prestat_dir_name" => func!(fd_prestat_dir_name), + "fd_pwrite" => func!(fd_pwrite), + "fd_read" => func!(fd_read), + "fd_readdir" => func!(fd_readdir), + "fd_renumber" => func!(fd_renumber), + "fd_seek" => func!(fd_seek), + "fd_sync" => func!(fd_sync), + "fd_tell" => func!(fd_tell), + "fd_write" => func!(fd_write), + "path_create_directory" => func!(path_create_directory), + "path_filestat_get" => func!(path_filestat_get), + "path_filestat_set_times" => func!(path_filestat_set_times), + "path_link" => func!(path_link), + "path_open" => func!(path_open), + "path_readlink" => func!(path_readlink), + "path_remove_directory" => func!(path_remove_directory), + "path_rename" => func!(path_rename), + "path_symlink" => func!(path_symlink), + "path_unlink_file" => func!(path_unlink_file), + "poll_oneoff" => func!(poll_oneoff), + "proc_exit" => func!(proc_exit), + "proc_raise" => func!(proc_raise), + "random_get" => func!(random_get), + "sched_yield" => func!(sched_yield), + "sock_recv" => func!(sock_recv), + "sock_send" => func!(sock_send), + "sock_shutdown" => func!(sock_shutdown), + }, + } +} diff --git a/lib/wasi/src/macros.rs b/lib/wasi/src/macros.rs new file mode 100644 index 000000000..1420b8f2d --- /dev/null +++ b/lib/wasi/src/macros.rs @@ -0,0 +1,19 @@ +macro_rules! wasi_try { + ($expr:expr) => {{ + let res: Result<_, crate::syscalls::types::__wasi_errno_t> = $expr; + match res { + Ok(val) => { + debug!("wasi::wasi_try::val: {:?}", val); + val + } + Err(err) => { + debug!("wasi::wasi_try::err: {:?}", err); + return err; + } + } + }}; + ($expr:expr; $e:expr) => {{ + let opt: Option<_> = $expr; + wasi_try!(opt.ok_or($e)) + }}; +} diff --git a/lib/wasi/src/ptr.rs b/lib/wasi/src/ptr.rs new file mode 100644 index 000000000..55a16d40c --- /dev/null +++ b/lib/wasi/src/ptr.rs @@ -0,0 +1,114 @@ +use crate::syscalls::types::{__wasi_errno_t, __WASI_EFAULT}; +use std::{cell::Cell, fmt, marker::PhantomData, mem}; +use wasmer_runtime_core::{ + memory::Memory, + types::{ValueType, WasmExternType}, +}; + +pub struct Array; +pub struct Item; + +#[repr(transparent)] +pub struct WasmPtr { + offset: u32, + _phantom: PhantomData<(T, Ty)>, +} + +impl WasmPtr { + #[inline] + pub fn new(offset: u32) -> Self { + Self { + offset, + _phantom: PhantomData, + } + } + + #[inline] + pub fn offset(self) -> u32 { + self.offset + } +} + +impl WasmPtr { + #[inline] + pub fn deref<'a>(self, memory: &'a Memory) -> Result<&'a Cell, __wasi_errno_t> { + if (self.offset as usize) + mem::size_of::() >= memory.size().bytes().0 { + return Err(__WASI_EFAULT); + } + unsafe { + // clears bits below aligment amount (assumes power of 2) to align pointer + let aligner = |ptr: usize, align: usize| ptr & !(align - 1); + let cell_ptr = aligner( + memory.view::().as_ptr().add(self.offset as usize) as usize, + mem::align_of::(), + ) as *const Cell; + Ok(&*cell_ptr) + } + } +} + +impl WasmPtr { + #[inline] + pub fn deref<'a>( + self, + memory: &'a Memory, + index: u32, + length: u32, + ) -> Result<&'a [Cell], __wasi_errno_t> { + if (self.offset as usize) + (mem::size_of::() * ((index + length) as usize)) + >= memory.size().bytes().0 + { + return Err(__WASI_EFAULT); + } + + unsafe { + let cell_ptrs = memory.view::().get_unchecked( + ((self.offset as usize) / mem::size_of::()) + (index as usize) + ..((self.offset() as usize) / mem::size_of::()) + + ((index + length) as usize), + ) as *const _; + Ok(&*cell_ptrs) + } + } +} + +unsafe impl WasmExternType for WasmPtr { + type Native = i32; + + fn to_native(self) -> Self::Native { + self.offset as i32 + } + fn from_native(n: Self::Native) -> Self { + Self { + offset: n as u32, + _phantom: PhantomData, + } + } +} + +unsafe impl ValueType for WasmPtr {} + +impl Clone for WasmPtr { + fn clone(&self) -> Self { + Self { + offset: self.offset, + _phantom: PhantomData, + } + } +} + +impl Copy for WasmPtr {} + +impl PartialEq for WasmPtr { + fn eq(&self, other: &Self) -> bool { + self.offset == other.offset + } +} + +impl Eq for WasmPtr {} + +impl fmt::Debug for WasmPtr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "WasmPtr({:#x})", self.offset) + } +} diff --git a/lib/wasi/src/state.rs b/lib/wasi/src/state.rs new file mode 100644 index 000000000..f1608e768 --- /dev/null +++ b/lib/wasi/src/state.rs @@ -0,0 +1,424 @@ +// use wasmer_runtime_abi::vfs::{ +// vfs::Vfs, +// file_like::{FileLike, Metadata}; +// }; +use crate::syscalls::types::*; +use generational_arena::{Arena, Index as Inode}; +use hashbrown::hash_map::{Entry, HashMap}; +use std::{ + cell::Cell, + fs, + io::{self, Read, Seek, Write}, + path::PathBuf, + time::SystemTime, +}; +use wasmer_runtime_core::debug; + +pub const MAX_SYMLINKS: usize = 100; + +#[derive(Debug)] +pub enum WasiFile { + HostFile(fs::File), +} + +impl Write for WasiFile { + fn write(&mut self, buf: &[u8]) -> io::Result { + match self { + WasiFile::HostFile(hf) => hf.write(buf), + } + } + + fn flush(&mut self) -> io::Result<()> { + match self { + WasiFile::HostFile(hf) => hf.flush(), + } + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + match self { + WasiFile::HostFile(hf) => hf.write_all(buf), + } + } + + fn write_fmt(&mut self, fmt: ::std::fmt::Arguments) -> io::Result<()> { + match self { + WasiFile::HostFile(hf) => hf.write_fmt(fmt), + } + } +} + +impl Read for WasiFile { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match self { + WasiFile::HostFile(hf) => hf.read(buf), + } + } + + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + match self { + WasiFile::HostFile(hf) => hf.read_to_end(buf), + } + } + + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + match self { + WasiFile::HostFile(hf) => hf.read_to_string(buf), + } + } + + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + match self { + WasiFile::HostFile(hf) => hf.read_exact(buf), + } + } +} + +impl Seek for WasiFile { + fn seek(&mut self, pos: io::SeekFrom) -> io::Result { + match self { + WasiFile::HostFile(hf) => hf.seek(pos), + } + } +} + +#[derive(Debug)] +pub struct InodeVal { + pub stat: __wasi_filestat_t, + pub is_preopened: bool, + pub name: String, + pub kind: Kind, +} + +impl InodeVal { + // TODO: clean this up + pub fn from_file_metadata( + metadata: &std::fs::Metadata, + name: String, + is_preopened: bool, + kind: Kind, + ) -> Self { + InodeVal { + stat: __wasi_filestat_t { + st_filetype: if metadata.is_dir() { + __WASI_FILETYPE_DIRECTORY + } else { + __WASI_FILETYPE_REGULAR_FILE + }, + st_size: metadata.len(), + st_atim: metadata + .accessed() + .ok() + .and_then(|sys_time| sys_time.duration_since(SystemTime::UNIX_EPOCH).ok()) + .map(|duration| duration.as_nanos() as u64) + .unwrap_or(0), + st_ctim: metadata + .created() + .ok() + .and_then(|sys_time| sys_time.duration_since(SystemTime::UNIX_EPOCH).ok()) + .map(|duration| duration.as_nanos() as u64) + .unwrap_or(0), + st_mtim: metadata + .modified() + .ok() + .and_then(|sys_time| sys_time.duration_since(SystemTime::UNIX_EPOCH).ok()) + .map(|duration| duration.as_nanos() as u64) + .unwrap_or(0), + ..__wasi_filestat_t::default() + }, + is_preopened, + name, + kind, + } + } +} + +#[allow(dead_code)] +#[derive(Debug)] +pub enum Kind { + File { + handle: WasiFile, + }, + Dir { + // TODO: wrap it like WasiFile + /// The path on the host system where the directory is located + path: PathBuf, + /// The entries of a directory are lazily filled. + entries: HashMap, + }, + Symlink { + forwarded: Inode, + }, + Buffer { + buffer: Vec, + }, +} + +#[derive(Clone, Debug)] +pub struct Fd { + pub rights: __wasi_rights_t, + pub rights_inheriting: __wasi_rights_t, + pub flags: __wasi_fdflags_t, + pub offset: u64, + pub inode: Inode, +} + +#[derive(Debug)] +pub struct WasiFs { + //pub repo: Repo, + pub name_map: HashMap, + pub inodes: Arena, + pub fd_map: HashMap, + pub next_fd: Cell, + pub inode_counter: Cell, +} + +impl WasiFs { + pub fn new(preopened_dirs: &[String]) -> Result { + /*let repo = RepoOpener::new() + .create(true) + .open("mem://wasmer-test-fs", "") + .map_err(|e| e.to_string())?;*/ + debug!("wasi::fs::inodes"); + let inodes = Arena::new(); + let mut wasi_fs = Self { + //repo: repo, + name_map: HashMap::new(), + inodes: inodes, + fd_map: HashMap::new(), + next_fd: Cell::new(3), + inode_counter: Cell::new(1000), + }; + for dir in preopened_dirs { + debug!("Attempting to preopen {}", &dir); + // TODO: think about this + let default_rights = 0x1FFFFFFF; // all rights + let cur_dir = PathBuf::from(dir); + let cur_dir_metadata = cur_dir.metadata().expect("Could not find directory"); + let kind = if cur_dir_metadata.is_dir() { + Kind::Dir { + path: cur_dir.clone(), + entries: Default::default(), + } + } else { + return Err(format!( + "WASI only supports pre-opened directories right now; found \"{}\"", + &dir + )); + }; + // TODO: handle nested pats in `file` + let inode_val = + InodeVal::from_file_metadata(&cur_dir_metadata, dir.clone(), true, kind); + + let inode = wasi_fs.inodes.insert(inode_val); + wasi_fs.inodes[inode].stat.st_ino = wasi_fs.inode_counter.get(); + wasi_fs + .create_fd(default_rights, default_rights, 0, inode) + .expect("Could not open fd"); + } + debug!("wasi::fs::end"); + Ok(wasi_fs) + } + + #[allow(dead_code)] + fn get_inode(&mut self, path: &str) -> Option { + Some(match self.name_map.entry(path.to_string()) { + Entry::Occupied(o) => *o.get(), + Entry::Vacant(_v) => { + return None; + // let file = if let Ok(file) = OpenOptions::new() + // .read(true) + // .write(true) + // .create(false) + // .open(&mut self.repo, path) + // { + // file + // } else { + // return None; + // }; + + // let metadata = file.metadata().unwrap(); + // let inode_index = { + // let index = self.inode_counter.get(); + // self.inode_counter.replace(index + 1) + // }; + + // let systime_to_nanos = |systime: SystemTime| { + // let duration = systime + // .duration_since(SystemTime::UNIX_EPOCH) + // .expect("should always be after unix epoch"); + // duration.as_nanos() as u64 + // }; + + // let inode = self.inodes.insert(InodeVal { + // stat: __wasi_filestat_t { + // st_dev: 0, + // st_ino: inode_index, + // st_filetype: match metadata.file_type() { + // FileType::File => __WASI_FILETYPE_REGULAR_FILE, + // FileType::Dir => __WASI_FILETYPE_DIRECTORY, + // }, + // st_nlink: 0, + // st_size: metadata.content_len() as u64, + // st_atim: systime_to_nanos(SystemTime::now()), + // st_mtim: systime_to_nanos(metadata.modified_at()), + // st_ctim: systime_to_nanos(metadata.created_at()), + // }, + // is_preopened: false, + // name: path.to_string(), + // kind: match metadata.file_type() { + // FileType::File => Kind::File { handle: file }, + // FileType::Dir => Kind::Dir { + // handle: file, + // entries: HashMap::new(), + // }, + // }, + // }); + // v.insert(inode); + // inode + } + }) + } + + #[allow(dead_code)] + fn filestat_inode( + &self, + inode: Inode, + flags: __wasi_lookupflags_t, + ) -> Result<__wasi_filestat_t, __wasi_errno_t> { + let inode_val = &self.inodes[inode]; + if let (true, Kind::Symlink { mut forwarded }) = + (flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0, &inode_val.kind) + { + // Time to follow the symlink. + let mut counter = 0; + + while counter <= MAX_SYMLINKS { + let inode_val = &self.inodes[forwarded]; + if let &Kind::Symlink { + forwarded: new_forwarded, + } = &inode_val.kind + { + counter += 1; + forwarded = new_forwarded; + } else { + return Ok(inode_val.stat); + } + } + + Err(__WASI_EMLINK) + } else { + Ok(inode_val.stat) + } + } + + #[allow(dead_code)] + pub fn filestat_path( + &mut self, + preopened_fd: __wasi_fd_t, + flags: __wasi_lookupflags_t, + path: &str, + ) -> Result<__wasi_filestat_t, __wasi_errno_t> { + warn!("Should use preopned_fd: {}", preopened_fd); + let inode = self.get_inode(path).ok_or(__WASI_EINVAL)?; + + self.filestat_inode(inode, flags) + } + + pub fn filestat_fd(&self, fd: __wasi_fd_t) -> Result<__wasi_filestat_t, __wasi_errno_t> { + let fd = self.fd_map.get(&fd).ok_or(__WASI_EBADF)?; + + Ok(self.inodes[fd.inode].stat) + } + + pub fn fdstat(&self, fd: __wasi_fd_t) -> Result<__wasi_fdstat_t, __wasi_errno_t> { + let fd = self.fd_map.get(&fd).ok_or(__WASI_EBADF)?; + + debug!("fdstat: {:?}", fd); + + Ok(__wasi_fdstat_t { + fs_filetype: match self.inodes[fd.inode].kind { + Kind::File { .. } => __WASI_FILETYPE_REGULAR_FILE, + Kind::Dir { .. } => __WASI_FILETYPE_DIRECTORY, + Kind::Symlink { .. } => __WASI_FILETYPE_SYMBOLIC_LINK, + _ => __WASI_FILETYPE_UNKNOWN, + }, + fs_flags: fd.flags, + fs_rights_base: fd.rights, + fs_rights_inheriting: fd.rights_inheriting, // TODO(lachlan): Is this right? + }) + } + + pub fn prestat_fd(&self, fd: __wasi_fd_t) -> Result<__wasi_prestat_t, __wasi_errno_t> { + let fd = self.fd_map.get(&fd).ok_or(__WASI_EBADF)?; + + debug!("in prestat_fd {:?}", fd); + let inode_val = &self.inodes[fd.inode]; + + if inode_val.is_preopened { + Ok(__wasi_prestat_t { + pr_type: __WASI_PREOPENTYPE_DIR, + u: PrestatEnum::Dir { + // REVIEW: + pr_name_len: inode_val.name.len() as u32 + 1, + } + .untagged(), + }) + } else { + Err(__WASI_EBADF) + } + } + + pub fn flush(&mut self, fd: __wasi_fd_t) -> Result<(), __wasi_errno_t> { + match fd { + 0 => (), + 1 => io::stdout().flush().map_err(|_| __WASI_EIO)?, + 2 => io::stderr().flush().map_err(|_| __WASI_EIO)?, + _ => { + let fd = self.fd_map.get(&fd).ok_or(__WASI_EBADF)?; + if fd.rights & __WASI_RIGHT_FD_DATASYNC == 0 { + return Err(__WASI_EACCES); + } + + let inode = &mut self.inodes[fd.inode]; + + match &mut inode.kind { + Kind::File { handle } => handle.flush().map_err(|_| __WASI_EIO)?, + // TODO: verify this behavior + Kind::Dir { .. } => return Err(__WASI_EISDIR), + Kind::Symlink { .. } => unimplemented!(), + Kind::Buffer { .. } => (), + } + } + } + Ok(()) + } + + pub fn create_fd( + &mut self, + rights: __wasi_rights_t, + rights_inheriting: __wasi_rights_t, + flags: __wasi_fdflags_t, + inode: Inode, + ) -> Result { + let idx = self.next_fd.get(); + self.next_fd.set(idx + 1); + self.fd_map.insert( + idx, + Fd { + rights, + rights_inheriting, + flags, + offset: 0, + inode, + }, + ); + Ok(idx) + } +} + +#[derive(Debug)] +pub struct WasiState<'a> { + pub fs: WasiFs, + pub args: &'a [Vec], + pub envs: &'a [Vec], +} diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs new file mode 100644 index 000000000..0389273fc --- /dev/null +++ b/lib/wasi/src/syscalls/mod.rs @@ -0,0 +1,1646 @@ +#![allow(unused)] +pub mod types; +#[cfg(any(target_os = "linux", target_os = "macos"))] +pub mod unix; +#[cfg(any(target_os = "windows"))] +pub mod windows; + +use self::types::*; +use crate::{ + ptr::{Array, WasmPtr}, + state::{Fd, InodeVal, Kind, WasiFile, WasiState, MAX_SYMLINKS}, + ExitCode, +}; +use rand::{thread_rng, Rng}; +use std::cell::Cell; +use std::convert::Infallible; +use std::io::{self, Read, Seek, Write}; +use wasmer_runtime_core::{debug, memory::Memory, vm::Ctx}; + +#[cfg(any(target_os = "linux", target_os = "macos"))] +pub use unix::*; + +#[cfg(any(target_os = "windows"))] +pub use windows::*; + +#[allow(clippy::mut_from_ref)] +fn get_wasi_state(ctx: &Ctx) -> &mut WasiState { + unsafe { &mut *(ctx.data as *mut WasiState) } +} + +fn write_bytes( + mut write_loc: T, + memory: &Memory, + iovs_arr_cell: &[Cell<__wasi_ciovec_t>], +) -> Result { + let mut bytes_written = 0; + for iov in iovs_arr_cell { + let iov_inner = iov.get(); + let bytes = iov_inner.buf.deref(memory, 0, iov_inner.buf_len)?; + write_loc + .write(&bytes.iter().map(|b_cell| b_cell.get()).collect::>()) + .map_err(|_| { + write_loc.flush(); + __WASI_EIO + })?; + + // TODO: handle failure more accurately + bytes_written += iov_inner.buf_len; + } + write_loc.flush(); + Ok(bytes_written) +} + +/// checks that `rights_check_set` is a subset of `rights_set` +fn has_rights(rights_set: __wasi_rights_t, rights_check_set: __wasi_rights_t) -> bool { + rights_set | rights_check_set == rights_set +} + +#[must_use] +fn write_buffer_array( + memory: &Memory, + from: &[Vec], + ptr_buffer: WasmPtr, Array>, + buffer: WasmPtr, +) -> __wasi_errno_t { + let ptrs = wasi_try!(ptr_buffer.deref(memory, 0, from.len() as u32)); + + let mut current_buffer_offset = 0; + for ((i, sub_buffer), ptr) in from.iter().enumerate().zip(ptrs.iter()) { + ptr.set(WasmPtr::new(buffer.offset() + current_buffer_offset)); + + let cells = + wasi_try!(buffer.deref(memory, current_buffer_offset, sub_buffer.len() as u32 + 1)); + + for (cell, &byte) in cells.iter().zip(sub_buffer.iter().chain([0].iter())) { + cell.set(byte); + } + current_buffer_offset += sub_buffer.len() as u32 + 1; + } + + __WASI_ESUCCESS +} + +/// ### `args_get()` +/// Read command-line argument data. +/// The sizes of the buffers should match that returned by [`args_sizes_get()`](#args_sizes_get). +/// Inputs: +/// - `char **argv` +/// A pointer to a buffer to write the argument pointers. +/// - `char *argv_buf` +/// A pointer to a buffer to write the argument string data. +/// +pub fn args_get( + ctx: &mut Ctx, + argv: WasmPtr, Array>, + argv_buf: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::args_get"); + let state = get_wasi_state(ctx); + let memory = ctx.memory(0); + + let result = write_buffer_array(memory, &*state.args, argv, argv_buf); + + debug!( + "=> args:\n{}", + state + .args + .iter() + .enumerate() + .map(|(i, v)| format!( + "{:>20}: {}", + i, + ::std::str::from_utf8(v).unwrap().to_string() + )) + .collect::>() + .join("\n") + ); + + result +} + +/// ### `args_sizes_get()` +/// Return command-line argument data sizes. +/// Outputs: +/// - `size_t *argc` +/// The number of arguments. +/// - `size_t *argv_buf_size` +/// The size of the argument string data. +pub fn args_sizes_get( + ctx: &mut Ctx, + argc: WasmPtr, + argv_buf_size: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::args_sizes_get"); + let memory = ctx.memory(0); + + let argc = wasi_try!(argc.deref(memory)); + let argv_buf_size = wasi_try!(argv_buf_size.deref(memory)); + + let state = get_wasi_state(ctx); + + let argc_val = state.args.len() as u32; + let argv_buf_size_val = state.args.iter().map(|v| v.len() as u32 + 1).sum(); + argc.set(argc_val); + argv_buf_size.set(argv_buf_size_val); + + debug!("=> argc={}, argv_buf_size={}", argc_val, argv_buf_size_val); + + __WASI_ESUCCESS +} + +/// ### `clock_res_get()` +/// Get the resolution of the specified clock +/// Input: +/// - `__wasi_clockid_t clock_id` +/// The ID of the clock to get the resolution of +/// Output: +/// - `__wasi_timestamp_t *resolution` +/// The resolution of the clock in nanoseconds +pub fn clock_res_get( + ctx: &mut Ctx, + clock_id: __wasi_clockid_t, + resolution: WasmPtr<__wasi_timestamp_t>, +) -> __wasi_errno_t { + debug!("wasi::clock_res_get"); + let memory = ctx.memory(0); + + let out_addr = wasi_try!(resolution.deref(memory)); + platform_clock_res_get(clock_id, out_addr) +} + +/// ### `clock_time_get()` +/// Get the time of the specified clock +/// Inputs: +/// - `__wasi_clockid_t clock_id` +/// The ID of the clock to query +/// - `__wasi_timestamp_t precision` +/// The maximum amount of error the reading may have +/// Output: +/// - `__wasi_timestamp_t *time` +/// The value of the clock in nanoseconds +pub fn clock_time_get( + ctx: &mut Ctx, + clock_id: __wasi_clockid_t, + precision: __wasi_timestamp_t, + time: WasmPtr<__wasi_timestamp_t>, +) -> __wasi_errno_t { + debug!("wasi::clock_time_get"); + let memory = ctx.memory(0); + + let out_addr = wasi_try!(time.deref(memory)); + platform_clock_time_get(clock_id, precision, out_addr) +} + +/// ### `environ_get()` +/// Read environment variable data. +/// The sizes of the buffers should match that returned by [`environ_sizes_get()`](#environ_sizes_get). +/// Inputs: +/// - `char **environ` +/// A pointer to a buffer to write the environment variable pointers. +/// - `char *environ_buf` +/// A pointer to a buffer to write the environment variable string data. +pub fn environ_get( + ctx: &mut Ctx, + environ: WasmPtr, Array>, + environ_buf: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::environ_get"); + let state = get_wasi_state(ctx); + let memory = ctx.memory(0); + + write_buffer_array(memory, &*state.args, environ, environ_buf) +} + +/// ### `environ_sizes_get()` +/// Return command-line argument data sizes. +/// Outputs: +/// - `size_t *environ_count` +/// The number of environment variables. +/// - `size_t *environ_buf_size` +/// The size of the environment variable string data. +pub fn environ_sizes_get( + ctx: &mut Ctx, + environ_count: WasmPtr, + environ_buf_size: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::environ_sizes_get"); + let memory = ctx.memory(0); + + let environ_count = wasi_try!(environ_count.deref(memory)); + let environ_buf_size = wasi_try!(environ_buf_size.deref(memory)); + + let state = get_wasi_state(ctx); + + environ_count.set(state.envs.len() as u32); + environ_buf_size.set(state.envs.iter().map(|v| v.len() as u32).sum()); + + __WASI_ESUCCESS +} + +/// ### `fd_advise()` +/// Advise the system about how a file will be used +/// Inputs: +/// - `__wasi_fd_t fd` +/// The file descriptor the advice applies to +/// - `__wasi_filesize_t offset` +/// The offset from which the advice applies +/// - `__wasi_filesize_t len` +/// The length from the offset to which the advice applies +/// - `__wasi_advice_t advice` +/// The advice to give +pub fn fd_advise( + ctx: &mut Ctx, + fd: __wasi_fd_t, + offset: __wasi_filesize_t, + len: __wasi_filesize_t, + advice: __wasi_advice_t, +) -> __wasi_errno_t { + debug!("wasi::fd_advise: fd={}", fd); + + // this is used for our own benefit, so just returning success is a valid + // implementation for now + __WASI_ESUCCESS +} + +/// ### `fd_allocate` +/// Allocate extra space for a file descriptor +/// Inputs: +/// - `__wasi_fd_t fd` +/// The file descriptor to allocate for +/// - `__wasi_filesize_t offset` +/// The offset from the start marking the beginning of the allocation +/// - `__wasi_filesize_t len` +/// The length from the offset marking the end of the allocation +pub fn fd_allocate( + ctx: &mut Ctx, + fd: __wasi_fd_t, + offset: __wasi_filesize_t, + len: __wasi_filesize_t, +) -> __wasi_errno_t { + debug!("wasi::fd_allocate"); + unimplemented!("wasi::fd_allocate") +} + +/// ### `fd_close()` +/// Close an open file descriptor +/// Inputs: +/// - `__wasi_fd_t fd` +/// A file descriptor mapping to an open file to close +/// Errors: +/// - `__WASI_EISDIR` +/// If `fd` is a directory +/// - `__WASI_EBADF` +/// If `fd` is invalid or not open (TODO: consider __WASI_EINVAL) +pub fn fd_close(ctx: &mut Ctx, fd: __wasi_fd_t) -> __wasi_errno_t { + debug!("wasi::fd_close"); + // FD is too large + return __WASI_EMFILE; + // FD is a directory (due to user input) + return __WASI_EISDIR; + // FD is invalid + return __WASI_EBADF; + __WASI_ESUCCESS +} + +/// ### `fd_datasync()` +/// Synchronize the file data to disk +/// Inputs: +/// - `__wasi_fd_t fd` +/// The file descriptor to sync +pub fn fd_datasync(ctx: &mut Ctx, fd: __wasi_fd_t) -> __wasi_errno_t { + debug!("wasi::fd_datasync"); + let state = get_wasi_state(ctx); + + if let Err(e) = state.fs.flush(fd) { + e + } else { + __WASI_ESUCCESS + } +} + +/// ### `fd_fdstat_get()` +/// Get metadata of a file descriptor +/// Input: +/// - `__wasi_fd_t fd` +/// The file descriptor whose metadata will be accessed +/// Output: +/// - `__wasi_fdstat_t *buf` +/// The location where the metadata will be written +pub fn fd_fdstat_get( + ctx: &mut Ctx, + fd: __wasi_fd_t, + buf_ptr: WasmPtr<__wasi_fdstat_t>, +) -> __wasi_errno_t { + debug!( + "wasi::fd_fdstat_get: fd={}, buf_ptr={}", + fd, + buf_ptr.offset() + ); + let mut state = get_wasi_state(ctx); + let memory = ctx.memory(0); + let stat = wasi_try!(state.fs.fdstat(fd)); + let buf = wasi_try!(buf_ptr.deref(memory)); + + buf.set(stat); + + __WASI_ESUCCESS +} + +/// ### `fd_fdstat_set_flags()` +/// Set file descriptor flags for a file descriptor +/// Inputs: +/// - `__wasi_fd_t fd` +/// The file descriptor to apply the new flags to +/// - `__wasi_fdflags_t flags` +/// The flags to apply to `fd` +pub fn fd_fdstat_set_flags( + ctx: &mut Ctx, + fd: __wasi_fd_t, + flags: __wasi_fdflags_t, +) -> __wasi_errno_t { + debug!("wasi::fd_fdstat_set_flags"); + let state = get_wasi_state(ctx); + let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + + if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_FDSTAT_SET_FLAGS) { + return __WASI_EACCES; + } + + fd_entry.flags = flags; + __WASI_ESUCCESS +} + +/// ### `fd_fdstat_set_rights()` +/// Set the rights of a file descriptor. This can only be used to remove rights +/// Inputs: +/// - `__wasi_fd_t fd` +/// The file descriptor to apply the new rights to +/// - `__wasi_rights_t fs_rights_base` +/// The rights to apply to `fd` +/// - `__wasi_rights_t fs_rights_inheriting` +/// The inheriting rights to apply to `fd` +pub fn fd_fdstat_set_rights( + ctx: &mut Ctx, + fd: __wasi_fd_t, + fs_rights_base: __wasi_rights_t, + fs_rights_inheriting: __wasi_rights_t, +) -> __wasi_errno_t { + debug!("wasi::fd_fdstat_set_rights"); + let state = get_wasi_state(ctx); + let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + + // ensure new rights are a subset of current rights + if fd_entry.rights | fs_rights_base != fd_entry.rights + || fd_entry.rights_inheriting | fs_rights_inheriting != fd_entry.rights_inheriting + { + return __WASI_ENOTCAPABLE; + } + + fd_entry.rights = fs_rights_base; + fd_entry.rights_inheriting = fs_rights_inheriting; + + __WASI_ESUCCESS +} + +/// ### `fd_filestat_get()` +/// Get the metadata of an open file +/// Input: +/// - `__wasi_fd_t fd` +/// The open file descriptor whose metadata will be read +/// Output: +/// - `__wasi_filestat_t *buf` +/// Where the metadata from `fd` will be written +pub fn fd_filestat_get( + ctx: &mut Ctx, + fd: __wasi_fd_t, + buf: WasmPtr<__wasi_filestat_t>, +) -> __wasi_errno_t { + debug!("wasi::fd_filestat_get"); + let mut state = get_wasi_state(ctx); + let memory = ctx.memory(0); + + let stat = wasi_try!(state.fs.filestat_fd(fd)); + + let buf = wasi_try!(buf.deref(memory)); + buf.set(stat); + + __WASI_ESUCCESS +} + +pub fn fd_filestat_set_size( + ctx: &mut Ctx, + fd: __wasi_fd_t, + st_size: __wasi_filesize_t, +) -> __wasi_errno_t { + debug!("wasi::fd_filestat_set_size"); + unimplemented!("wasi::fd_filestat_set_size") +} + +/// ### `fd_filestat_set_times()` +/// Set timestamp metadata on a file +/// Inputs: +/// - `__wasi_timestamp_t st_atim` +/// Last accessed time +/// - `__wasi_timestamp_t st_mtim` +/// Last modified time +/// - `__wasi_fstflags_t fst_flags` +/// Bit-vector for controlling which times get set +pub fn fd_filestat_set_times( + ctx: &mut Ctx, + fd: __wasi_fd_t, + st_atim: __wasi_timestamp_t, + st_mtim: __wasi_timestamp_t, + fst_flags: __wasi_fstflags_t, +) -> __wasi_errno_t { + debug!("wasi::fd_filestat_set_times"); + let state = get_wasi_state(ctx); + let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + + if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_FILESTAT_SET_TIMES) { + return __WASI_EACCES; + } + + if (fst_flags & __WASI_FILESTAT_SET_ATIM != 0 && fst_flags & __WASI_FILESTAT_SET_ATIM_NOW != 0) + || (fst_flags & __WASI_FILESTAT_SET_MTIM != 0 + && fst_flags & __WASI_FILESTAT_SET_MTIM_NOW != 0) + { + return __WASI_EINVAL; + } + + let inode = &mut state.fs.inodes[fd_entry.inode]; + + if fst_flags & __WASI_FILESTAT_SET_ATIM != 0 { + inode.stat.st_atim = st_atim; + } else if fst_flags & __WASI_FILESTAT_SET_ATIM_NOW != 0 { + // set to current real time + unimplemented!("Set filestat time to the current real time"); + } + + if fst_flags & __WASI_FILESTAT_SET_MTIM != 0 { + inode.stat.st_mtim = st_mtim; + } else if fst_flags & __WASI_FILESTAT_SET_MTIM_NOW != 0 { + // set to current real time + unimplemented!("Set filestat time to the current real time"); + } + + __WASI_ESUCCESS +} + +pub fn fd_pread( + ctx: &mut Ctx, + fd: __wasi_fd_t, + iovs: WasmPtr<__wasi_iovec_t, Array>, + iovs_len: u32, + offset: __wasi_filesize_t, + nread: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::fd_pread"); + let memory = ctx.memory(0); + + let iov_cells = wasi_try!(iovs.deref(memory, 0, iovs_len)); + let nread_cell = wasi_try!(nread.deref(memory)); + + unimplemented!("wasi::fd_pread"); + + __WASI_ESUCCESS +} + +/// ### `fd_prestat_get()` +/// Get metadata about a preopened file descriptor +/// Input: +/// - `__wasi_fd_t fd` +/// The preopened file descriptor to query +/// Output: +/// - `__wasi_prestat *buf` +/// Where the metadata will be written +pub fn fd_prestat_get( + ctx: &mut Ctx, + fd: __wasi_fd_t, + buf: WasmPtr<__wasi_prestat_t>, +) -> __wasi_errno_t { + debug!("wasi::fd_prestat_get: fd={}", fd); + let memory = ctx.memory(0); + + let prestat_ptr = wasi_try!(buf.deref(memory)); + + let state = get_wasi_state(ctx); + prestat_ptr.set(wasi_try!(state.fs.prestat_fd(fd))); + + __WASI_ESUCCESS +} + +pub fn fd_prestat_dir_name( + ctx: &mut Ctx, + fd: __wasi_fd_t, + path: WasmPtr, + path_len: u32, +) -> __wasi_errno_t { + debug!( + "wasi::fd_prestat_dir_name: fd={}, path_len={}", + fd, path_len + ); + let memory = ctx.memory(0); + let path_chars = wasi_try!(path.deref(memory, 0, path_len)); + + let state = get_wasi_state(ctx); + let real_fd = wasi_try!(state.fs.fd_map.get(&fd).ok_or(__WASI_EBADF)); + let inode_val = &state.fs.inodes[real_fd.inode]; + + // check inode-val.is_preopened? + + if let Kind::Dir { .. } = inode_val.kind { + // TODO: verify this: null termination, etc + if inode_val.name.len() <= path_len as usize { + let mut i = 0; + for c in inode_val.name.bytes() { + path_chars[i].set(c); + i += 1 + } + path_chars[i].set(0); + + debug!( + "=> result: \"{}\"", + ::std::str::from_utf8(unsafe { &*(&path_chars[..] as *const [_] as *const [u8]) }) + .unwrap() + ); + + __WASI_ESUCCESS + } else { + __WASI_EOVERFLOW + } + } else { + __WASI_ENOTDIR + } +} + +/// ### `fd_pwrite()` +/// Write to a file without adjusting its offset +/// Inputs: +/// - `__wasi_fd_t` +/// File descriptor (opened with writing) to write to +/// - `const __wasi_ciovec_t *iovs` +/// List of vectors to read data from +/// - `u32 iovs_len` +/// Length of data in `iovs` +/// - `__wasi_filesize_t offset` +/// The offset to write at +/// Output: +/// - `u32 *nwritten` +/// Number of bytes written +pub fn fd_pwrite( + ctx: &mut Ctx, + fd: __wasi_fd_t, + iovs: WasmPtr<__wasi_ciovec_t, Array>, + iovs_len: u32, + offset: __wasi_filesize_t, + nwritten: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::fd_pwrite"); + // TODO: refactor, this is just copied from `fd_write`... + let memory = ctx.memory(0); + let iovs_arr_cell = wasi_try!(iovs.deref(memory, 0, iovs_len)); + let nwritten_cell = wasi_try!(nwritten.deref(memory)); + + let bytes_written = match fd { + 0 => return __WASI_EINVAL, + 1 => { + let stdout = io::stdout(); + let mut handle = stdout.lock(); + + wasi_try!(write_bytes(handle, memory, iovs_arr_cell)) + } + + 2 => { + let stderr = io::stderr(); + let mut handle = stderr.lock(); + + wasi_try!(write_bytes(handle, memory, iovs_arr_cell)) + } + _ => { + let state = get_wasi_state(ctx); + let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + + if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_WRITE) { + // TODO: figure out the error to return when lacking rights + return __WASI_EACCES; + } + + let inode = &mut state.fs.inodes[fd_entry.inode]; + + let bytes_written = match &mut inode.kind { + Kind::File { handle } => { + handle.seek(::std::io::SeekFrom::Start(offset as u64)); + wasi_try!(write_bytes(handle, memory, iovs_arr_cell)) + } + Kind::Dir { .. } => { + // TODO: verify + return __WASI_EISDIR; + } + Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_pwrite"), + Kind::Buffer { buffer } => wasi_try!(write_bytes( + &mut buffer[(offset as usize)..], + memory, + iovs_arr_cell + )), + }; + + bytes_written + } + }; + + nwritten_cell.set(bytes_written); + + __WASI_ESUCCESS +} + +/// ### `fd_read()` +/// Read data from file descriptor +/// Inputs: +/// - `__wasi_fd_t fd` +/// File descriptor from which data will be read +/// - `const __wasi_iovec_t *iovs` +/// Vectors where data will be stored +/// - `u32 iovs_len` +/// Length of data in `iovs` +/// Output: +/// - `u32 *nread` +/// Number of bytes read +pub fn fd_read( + ctx: &mut Ctx, + fd: __wasi_fd_t, + iovs: WasmPtr<__wasi_iovec_t, Array>, + iovs_len: u32, + nread: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::fd_read: fd={}", fd); + let memory = ctx.memory(0); + + let iovs_arr_cell = wasi_try!(iovs.deref(memory, 0, iovs_len)); + let nread_cell = wasi_try!(nread.deref(memory)); + + fn read_bytes( + mut reader: T, + memory: &Memory, + iovs_arr_cell: &[Cell<__wasi_iovec_t>], + ) -> Result { + let mut bytes_read = 0; + + for iov in iovs_arr_cell { + let iov_inner = iov.get(); + let bytes = iov_inner.buf.deref(memory, 0, iov_inner.buf_len)?; + let mut raw_bytes: &mut [u8] = + unsafe { &mut *(bytes as *const [_] as *mut [_] as *mut [u8]) }; + bytes_read += reader.read(raw_bytes).map_err(|_| __WASI_EIO)? as u32; + } + Ok(bytes_read) + } + + let bytes_read = match fd { + 0 => { + let stdin = io::stdin(); + let mut handle = stdin.lock(); + + wasi_try!(read_bytes(handle, memory, iovs_arr_cell)) + } + 1 | 2 => return __WASI_EINVAL, + _ => { + let state = get_wasi_state(ctx); + let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + + if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_READ) { + // TODO: figure out the error to return when lacking rights + return __WASI_EACCES; + } + + let offset = fd_entry.offset as usize; + let inode = &mut state.fs.inodes[fd_entry.inode]; + + let bytes_read = match &mut inode.kind { + Kind::File { handle } => { + handle.seek(::std::io::SeekFrom::Start(offset as u64)); + wasi_try!(read_bytes(handle, memory, iovs_arr_cell)) + } + Kind::Dir { .. } => { + // TODO: verify + return __WASI_EISDIR; + } + Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_read"), + Kind::Buffer { buffer } => { + wasi_try!(read_bytes(&buffer[offset..], memory, iovs_arr_cell)) + } + }; + + fd_entry.offset += bytes_read as u64; + + bytes_read + } + }; + + nread_cell.set(bytes_read); + + __WASI_ESUCCESS +} + +/// ### `fd_readdir()` +/// Read data from directory specified by file descriptor +/// Inputs: +/// - `__wasi_fd_t fd` +/// File descriptor from which directory data will be read +/// - `void *buf` +/// Buffer where directory entries are stored +/// - `u32 buf_len` +/// Length of data in `buf` +/// - `__wasi_dircookie_t cookie` +/// Where the directory reading should start from +/// Output: +/// - `u32 *bufused` +/// The Number of bytes stored in `buf`; if less than `buf_len` then entire +/// directory has been read +pub fn fd_readdir( + ctx: &mut Ctx, + fd: __wasi_fd_t, + buf: WasmPtr, + buf_len: u32, + cookie: __wasi_dircookie_t, + bufused: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::fd_readdir"); + let memory = ctx.memory(0); + + if let (Ok(buf_arr_cell), Ok(bufused_cell)) = + (buf.deref(memory, 0, buf_len), bufused.deref(memory)) + { + unimplemented!("wasi::fd_readdir") + } else { + __WASI_EFAULT + } +} + +/// ### `fd_renumber()` +/// Atomically copy file descriptor +/// Inputs: +/// - `__wasi_fd_t from` +/// File descriptor to copy +/// - `__wasi_fd_t to` +/// Location to copy file descriptor to +pub fn fd_renumber(ctx: &mut Ctx, from: __wasi_fd_t, to: __wasi_fd_t) -> __wasi_errno_t { + debug!("wasi::fd_renumber: from={}, to={}", from, to); + let state = get_wasi_state(ctx); + let fd_entry = wasi_try!(state.fs.fd_map.get(&from).ok_or(__WASI_EBADF)); + + state.fs.fd_map.insert( + to, + Fd { + // TODO: verify this is correct + rights: fd_entry.rights_inheriting, + ..*fd_entry + }, + ); + __WASI_ESUCCESS +} + +/// ### `fd_seek()` +/// Update file descriptor offset +/// Inputs: +/// - `__wasi_fd_t fd` +/// File descriptor to mutate +/// - `__wasi_filedelta_t offset` +/// Number of bytes to adjust offset by +/// - `__wasi_whence_t whence` +/// What the offset is relative to +/// Output: +/// - `__wasi_filesize_t *fd` +/// The new offset relative to the start of the file +pub fn fd_seek( + ctx: &mut Ctx, + fd: __wasi_fd_t, + offset: __wasi_filedelta_t, + whence: __wasi_whence_t, + newoffset: WasmPtr<__wasi_filesize_t>, +) -> __wasi_errno_t { + debug!("wasi::fd_seek: fd={}, offset={}", fd, offset); + let memory = ctx.memory(0); + let state = get_wasi_state(ctx); + let new_offset_cell = wasi_try!(newoffset.deref(memory)); + + let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + + if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_SEEK) { + return __WASI_EACCES; + } + + // TODO: handle case if fd is a dir? + match whence { + __WASI_WHENCE_CUR => fd_entry.offset = (fd_entry.offset as i64 + offset) as u64, + __WASI_WHENCE_END => unimplemented!("__WASI__WHENCE_END in wasi::fd_seek"), + __WASI_WHENCE_SET => fd_entry.offset = offset as u64, + _ => return __WASI_EINVAL, + } + + new_offset_cell.set(fd_entry.offset); + + __WASI_ESUCCESS +} + +/// ### `fd_sync()` +/// Synchronize file and metadata to disk (TODO: expand upon what this means in our system) +/// Inputs: +/// - `__wasi_fd_t fd` +/// The file descriptor to sync +/// Errors: +/// TODO: figure out which errors this should return +/// - `__WASI_EPERM` +/// - `__WASI_ENOTCAPABLE` +pub fn fd_sync(ctx: &mut Ctx, fd: __wasi_fd_t) -> __wasi_errno_t { + debug!("wasi::fd_sync"); + // TODO: check __WASI_RIGHT_FD_SYNC + unimplemented!("wasi::fd_sync") +} + +/// ### `fd_tell()` +/// Get the offset of the file descriptor +/// Inputs: +/// - `__wasi_fd_t fd` +/// The file descriptor to access +/// Output: +/// - `__wasi_filesize_t *offset` +/// The offset of `fd` relative to the start of the file +pub fn fd_tell( + ctx: &mut Ctx, + fd: __wasi_fd_t, + offset: WasmPtr<__wasi_filesize_t>, +) -> __wasi_errno_t { + debug!("wasi::fd_tell"); + let memory = ctx.memory(0); + let state = get_wasi_state(ctx); + let offset_cell = wasi_try!(offset.deref(memory)); + + let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + + if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_TELL) { + return __WASI_EACCES; + } + + offset_cell.set(fd_entry.offset); + + __WASI_ESUCCESS +} + +/// ### `fd_write()` +/// Write data to the file descriptor +/// Inputs: +/// - `__wasi_fd_t` +/// File descriptor (opened with writing) to write to +/// - `const __wasi_ciovec_t *iovs` +/// List of vectors to read data from +/// - `u32 iovs_len` +/// Length of data in `iovs` +/// Output: +/// - `u32 *nwritten` +/// Number of bytes written +/// Errors: +/// +pub fn fd_write( + ctx: &mut Ctx, + fd: __wasi_fd_t, + iovs: WasmPtr<__wasi_ciovec_t, Array>, + iovs_len: u32, + nwritten: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::fd_write: fd={}", fd); + let memory = ctx.memory(0); + let iovs_arr_cell = wasi_try!(iovs.deref(memory, 0, iovs_len)); + let nwritten_cell = wasi_try!(nwritten.deref(memory)); + + let bytes_written = match fd { + 0 => return __WASI_EINVAL, + 1 => { + let stdout = io::stdout(); + let mut handle = stdout.lock(); + + wasi_try!(write_bytes(handle, memory, iovs_arr_cell)) + } + + 2 => { + let stderr = io::stderr(); + let mut handle = stderr.lock(); + + wasi_try!(write_bytes(handle, memory, iovs_arr_cell)) + } + _ => { + let state = get_wasi_state(ctx); + let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + + if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_WRITE) { + // TODO: figure out the error to return when lacking rights + return __WASI_EACCES; + } + + let offset = fd_entry.offset as usize; + let inode = &mut state.fs.inodes[fd_entry.inode]; + + let bytes_written = match &mut inode.kind { + Kind::File { handle } => { + handle.seek(::std::io::SeekFrom::Start(offset as u64)); + + wasi_try!(write_bytes(handle, memory, iovs_arr_cell)) + } + Kind::Dir { .. } => { + // TODO: verify + return __WASI_EISDIR; + } + Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_write"), + Kind::Buffer { buffer } => { + wasi_try!(write_bytes(&mut buffer[offset..], memory, iovs_arr_cell)) + } + }; + + fd_entry.offset += bytes_written as u64; + + bytes_written + } + }; + + nwritten_cell.set(bytes_written); + + __WASI_ESUCCESS +} + +/// ### `path_create_directory()` +/// Create directory at a path +/// Inputs: +/// - `__wasi_fd_t fd` +/// The directory that the path is relative to +/// - `const char *path` +/// String containing path data +/// - `u32 path_len` +/// The length of `path` +/// Errors: +/// Required Rights: +/// - __WASI_RIGHT_PATH_CREATE_DIRECTORY +/// This right must be set on the directory that the file is created in (TODO: verify that this is true) +pub fn path_create_directory( + ctx: &mut Ctx, + fd: __wasi_fd_t, + path: WasmPtr, + path_len: u32, +) -> __wasi_errno_t { + debug!("wasi::path_create_directory"); + let memory = ctx.memory(0); + let state = get_wasi_state(ctx); + + let working_dir = wasi_try!(state.fs.fd_map.get(&fd).ok_or(__WASI_EBADF)); + if !has_rights(working_dir.rights, __WASI_RIGHT_PATH_CREATE_DIRECTORY) { + return __WASI_EACCES; + } + let path_cells = wasi_try!(path.deref(memory, 0, path_len)); + let path_string = + wasi_try!( + std::str::from_utf8(unsafe { &*(path_cells as *const [_] as *const [u8]) }) + .map_err(|_| __WASI_EINVAL) + ); + debug!("=> path: {}", &path_string); + + let path = std::path::PathBuf::from(path_string); + let path_vec = wasi_try!(path + .components() + .map(|comp| { + comp.as_os_str() + .to_str() + .map(|inner_str| inner_str.to_string()) + .ok_or(__WASI_EINVAL) + }) + .collect::, __wasi_errno_t>>()); + if path_vec.is_empty() { + return __WASI_EINVAL; + } + + assert!( + path_vec.len() == 1, + "path_create_directory for paths greater than depth 1 has not been implemented because our WASI FS abstractions are a work in progress. We apologize for the inconvenience" + ); + debug!("Path vec: {:#?}", path_vec); + + wasi_try!(std::fs::create_dir(&path).map_err(|_| __WASI_EIO)); + + let kind = Kind::Dir { + //parent: Some(working_dir.inode), + path: path.clone(), + entries: Default::default(), + }; + let new_inode = state.fs.inodes.insert(InodeVal { + stat: __wasi_filestat_t::default(), + is_preopened: false, + name: path_vec[0].clone(), + kind, + }); + + if let Kind::Dir { entries, .. } = &mut state.fs.inodes[working_dir.inode].kind { + entries.insert(path_vec[0].clone(), new_inode); + } else { + return __WASI_ENOTDIR; + } + + __WASI_ESUCCESS +} + +/// ### `path_filestat_get()` +/// Access metadata about a file or directory +/// Inputs: +/// - `__wasi_fd_t fd` +/// The directory that `path` is relative to +/// - `__wasi_lookupflags_t flags` +/// Flags to control how `path` is understood +/// - `const char *path` +/// String containing the file path +/// - `u32 path_len` +/// The length of the `path` string +/// Output: +/// - `__wasi_file_stat_t *buf` +/// The location where the metadata will be stored +pub fn path_filestat_get( + ctx: &mut Ctx, + fd: __wasi_fd_t, + flags: __wasi_lookupflags_t, + path: WasmPtr, + path_len: u32, + buf: WasmPtr<__wasi_filestat_t>, +) -> __wasi_errno_t { + debug!("wasi::path_filestat_get"); + let state = get_wasi_state(ctx); + let memory = ctx.memory(0); + + let root_dir = wasi_try!(state.fs.fd_map.get(&fd).ok_or(__WASI_EBADF)); + + if !has_rights(root_dir.rights, __WASI_RIGHT_PATH_FILESTAT_GET) { + return __WASI_EACCES; + } + + let path_string = wasi_try!(::std::str::from_utf8(unsafe { + &*(wasi_try!(path.deref(memory, 0, path_len)) as *const [_] as *const [u8]) + }) + .map_err(|_| __WASI_EINVAL)); + debug!("=> path: {}", &path_string); + let path = std::path::PathBuf::from(path_string); + let path_vec = path + .components() + .map(|comp| comp.as_os_str().to_string_lossy().to_string()) + .collect::>(); + let buf_cell = wasi_try!(buf.deref(memory)); + + if path_vec.is_empty() { + return __WASI_EINVAL; + } + let mut cumulative_path = std::path::PathBuf::new(); + + debug!("=> Path vec: {:?}:", &path_vec); + // find the inode by traversing the path + let mut inode = root_dir.inode; + 'outer: for segment in &path_vec[..(path_vec.len() - 1)] { + // loop to traverse symlinks + // TODO: proper cycle detection + let mut sym_count = 0; + loop { + match &state.fs.inodes[inode].kind { + Kind::Dir { entries, .. } => { + cumulative_path.push(&segment); + if let Some(entry) = entries.get(segment) { + debug!("Entry {:?} found", &segment); + inode = entry.clone(); + } else { + // lazily load + debug!("Lazily loading entry {:?}", &segment); + let path_metadata = + wasi_try!(cumulative_path.metadata().map_err(|_| __WASI_ENOENT)); + if !path_metadata.is_dir() { + // TODO: should this just return invalid arg? + return __WASI_ENOTDIR; + } + let kind = Kind::Dir { + path: std::path::PathBuf::from(&segment), + entries: Default::default(), + }; + let inode_val = InodeVal::from_file_metadata( + &path_metadata, + segment.clone(), + false, + kind, + ); + let new_inode = state.fs.inodes.insert(inode_val); + let inode_idx = state.fs.inode_counter.get(); + state.fs.inode_counter.replace(inode_idx + 1); + if let Kind::Dir { entries, .. } = &mut state.fs.inodes[inode].kind { + // check that we're not displacing any entries + assert!(entries.insert(segment.clone(), new_inode).is_none()); + state.fs.inodes[new_inode].stat.st_ino = state.fs.inode_counter.get(); + inode = new_inode; + } + debug!("Directory {:#?} lazily loaded", &cumulative_path); + } + continue 'outer; + } + Kind::Symlink { forwarded } => { + // TODO: updated cumulative path + sym_count += 1; + inode = forwarded.clone(); + if sym_count > MAX_SYMLINKS { + return __WASI_ELOOP; + } + } + _ => { + return __WASI_ENOTDIR; + } + } + } + } + + let final_inode = match &state.fs.inodes[inode].kind { + Kind::Dir { path, entries, .. } => { + // TODO: fail earlier if size 0 + let last_segment = path_vec.last().unwrap(); + cumulative_path.push(last_segment); + + if entries.contains_key(last_segment) { + entries[last_segment] + } else { + // lazily load it if we can + if !cumulative_path.exists() { + return __WASI_ENOENT; + } + let final_path_metadata = + wasi_try!(cumulative_path.metadata().map_err(|_| __WASI_EIO)); + let new_inode = if final_path_metadata.is_dir() { + debug!("Opening host directory {:#?}", &cumulative_path); + state.fs.inodes.insert(InodeVal { + stat: __wasi_filestat_t::default(), + is_preopened: false, // is this correct? + name: last_segment.clone(), + kind: Kind::Dir { + path: std::path::PathBuf::from(&last_segment), + entries: Default::default(), + }, + }) + } else { + debug!("Opening host file {:#?}", &cumulative_path); + let real_open_file = wasi_try!(std::fs::OpenOptions::new() + .read(true) + .write(true) + .open(&cumulative_path) + .map_err(|_| __WASI_ENOENT)); + + state.fs.inodes.insert(InodeVal { + stat: __wasi_filestat_t::default(), + is_preopened: false, // is this correct? + name: last_segment.clone(), + kind: Kind::File { + handle: WasiFile::HostFile(real_open_file), + }, + }) + }; + // reborrow to insert entry + if let Kind::Dir { entries, .. } = &mut state.fs.inodes[inode].kind { + entries.insert(last_segment.clone(), new_inode); + } + new_inode + } + } + _ => { + return __WASI_ENOTDIR; + } + }; + + let stat = state.fs.inodes[final_inode].stat; + + buf_cell.set(stat); + + __WASI_ESUCCESS +} + +/// ### `path_filestat_set_times()` +/// Update time metadata on a file or directory +/// Inputs: +/// - `__wasi_fd_t fd` +/// The directory relative to which the path is resolved +/// - `__wasi_lookupflags_t flags` +/// Flags to control how the path is understood +/// - `const char *path` +/// String containing the file path +/// - `u32 path_len` +/// The length of the `path` string +/// - `__wasi_timestamp_t st_atim` +/// The timestamp that the last accessed time attribute is set to +/// - `__wasi_timestamp_t st_mtim` +/// The timestamp that the last modified time attribute is set to +/// - `__wasi_fstflags_t fst_flags` +/// A bitmask controlling which attributes are set +pub fn path_filestat_set_times( + ctx: &mut Ctx, + fd: __wasi_fd_t, + flags: __wasi_lookupflags_t, + path: WasmPtr, + path_len: u32, + st_atim: __wasi_timestamp_t, + st_mtim: __wasi_timestamp_t, + fst_flags: __wasi_fstflags_t, +) -> __wasi_errno_t { + debug!("wasi::path_filestat_set_times"); + unimplemented!("wasi::path_filestat_set_times") +} + +/// ### `path_link()` +/// Create a hard link +/// Inputs: +/// - `__wasi_fd_t old_fd` +/// The directory relative to which the `old_path` is +/// - `__wasi_lookupflags_t old_flags` +/// Flags to control how `old_path` is understood +/// - `const char *old_path` +/// String containing the old file path +/// - `u32 old_path_len` +/// Length of the `old_path` string +/// - `__wasi_fd_t new_fd` +/// The directory relative to which the `new_path` is +/// - `const char *new_path` +/// String containing the new file path +/// - `u32 old_path_len` +/// Length of the `new_path` string +pub fn path_link( + ctx: &mut Ctx, + old_fd: __wasi_fd_t, + old_flags: __wasi_lookupflags_t, + old_path: WasmPtr, + old_path_len: u32, + new_fd: __wasi_fd_t, + new_path: WasmPtr, + new_path_len: u32, +) -> __wasi_errno_t { + debug!("wasi::path_link"); + unimplemented!("wasi::path_link") +} + +/// ### `path_open()` +/// Open file located at the given path +/// Inputs: +/// - `__wasi_fd_t dirfd` +/// The fd corresponding to the directory that the file is in +/// - `__wasi_lookupflags_t dirflags` +/// Flags specifying how the path will be resolved +/// - `char *path` +/// The path of the file or directory to open +/// - `u32 path_len` +/// The length of the `path` string +/// - `__wasi_oflags_t o_flags` +/// How the file will be opened +/// - `__wasi_rights_t fs_rights_base` +/// The rights of the created file descriptor +/// - `__wasi_rights_t fs_rightsinheriting` +/// The rights of file descriptors derived from the created file descriptor +/// - `__wasi_fdflags_t fs_flags` +/// The flags of the file descriptor +/// Output: +/// - `__wasi_fd_t* fd` +/// The new file descriptor +/// Possible Errors: +/// - `__WASI_EACCES`, `__WASI_EBADF`, `__WASI_EFAULT`, `__WASI_EFBIG?`, `__WASI_EINVAL`, `__WASI_EIO`, `__WASI_ELOOP`, `__WASI_EMFILE`, `__WASI_ENAMETOOLONG?`, `__WASI_ENFILE`, `__WASI_ENOENT`, `__WASI_ENOTDIR`, `__WASI_EROFS`, and `__WASI_ENOTCAPABLE` +pub fn path_open( + ctx: &mut Ctx, + dirfd: __wasi_fd_t, + dirflags: __wasi_lookupflags_t, + path: WasmPtr, + path_len: u32, + o_flags: __wasi_oflags_t, + fs_rights_base: __wasi_rights_t, + fs_rights_inheriting: __wasi_rights_t, + fs_flags: __wasi_fdflags_t, + fd: WasmPtr<__wasi_fd_t>, +) -> __wasi_errno_t { + debug!("wasi::path_open"); + let memory = ctx.memory(0); + /* TODO: find actual upper bound on name size (also this is a path, not a name :think-fish:) */ + if path_len > 1024 * 1024 { + return __WASI_ENAMETOOLONG; + } + + let fd_cell = wasi_try!(fd.deref(memory)); + let path_cells = wasi_try!(path.deref(memory, 0, path_len)); + let state = get_wasi_state(ctx); + + // o_flags: + // - __WASI_O_FLAG_CREAT (create if it does not exist) + // - __WASI_O_DIRECTORY (fail if not dir) + // - __WASI_O_EXCL (fail if file exists) + // - __WASI_O_TRUNC (truncate size to 0) + + let working_dir = wasi_try!(state.fs.fd_map.get(&dirfd).ok_or(__WASI_EBADF)); + + // ASSUMPTION: open rights apply recursively + if !has_rights(working_dir.rights, __WASI_RIGHT_PATH_OPEN) { + return __WASI_EACCES; + } + + let path_string = + wasi_try!( + std::str::from_utf8(unsafe { &*(path_cells as *const [_] as *const [u8]) }) + .map_err(|_| __WASI_EINVAL) + ); + let path = std::path::PathBuf::from(path_string); + let path_vec = wasi_try!(path + .components() + .map(|comp| { + comp.as_os_str() + .to_str() + .map(|inner_str| inner_str.to_string()) + .ok_or(__WASI_EINVAL) + }) + .collect::, __wasi_errno_t>>()); + debug!("Path vec: {:#?}", path_vec); + + if path_vec.is_empty() { + return __WASI_EINVAL; + } + + let mut cur_dir_inode = working_dir.inode; + let mut cumulative_path = std::path::PathBuf::from("."); + + // traverse path + if path_vec.len() > 1 { + for path_segment in &path_vec[..(path_vec.len() - 1)] { + match &state.fs.inodes[cur_dir_inode].kind { + Kind::Dir { entries, .. } => { + if let Some(child) = entries.get(path_segment) { + cumulative_path.push(path_segment); + let inode_val = *child; + cur_dir_inode = inode_val; + } else { + // attempt to lazily load or create + if path_segment == ".." { + unimplemented!( + "\"..\" in paths in `path_open` has not been implemented yet" + ); + } + // lazily load + cumulative_path.push(path_segment); + let mut open_options = std::fs::OpenOptions::new(); + let open_options = open_options.read(true); + // ASSUMPTION: __WASI_O_CREAT applies recursively + let open_options = if o_flags & __WASI_O_CREAT != 0 { + open_options.create(true) + } else { + open_options + }; + // TODO: handle __WASI_O_TRUNC on directories + + // TODO: refactor and reuse + let cur_file_metadata = + wasi_try!(cumulative_path.metadata().map_err(|_| __WASI_EINVAL)); + let kind = if cur_file_metadata.is_dir() { + Kind::Dir { + path: cumulative_path.clone(), + entries: Default::default(), + } + } else { + return __WASI_ENOTDIR; + }; + let inode_val = InodeVal::from_file_metadata( + &cur_file_metadata, + path_segment.clone(), + false, + kind, + ); + + let new_inode = state.fs.inodes.insert(inode_val); + let inode_idx = state.fs.inode_counter.get(); + state.fs.inode_counter.replace(inode_idx + 1); + // reborrow to insert entry + if let Kind::Dir { entries, .. } = &mut state.fs.inodes[cur_dir_inode].kind + { + assert!(entries.insert(path_segment.clone(), new_inode).is_none()); + state.fs.inodes[new_inode].stat.st_ino = state.fs.inode_counter.get(); + cur_dir_inode = new_inode; + } + } + } + Kind::Symlink { .. } => unimplemented!("Symlinks not yet supported in `path_open`"), + _ => return __WASI_ENOTDIR, + } + } + } + + let file_name = path_vec.last().unwrap(); + + debug!( + "Looking for file {} in directory {:#?}", + file_name, cumulative_path + ); + cumulative_path.push(file_name); + let file_path = cumulative_path; + + let out_fd = if let Kind::Dir { entries, .. } = &mut state.fs.inodes[cur_dir_inode].kind { + if let Some(child) = entries.get(file_name).cloned() { + let child_inode_val = &state.fs.inodes[child]; + // early return based on flags + if o_flags & __WASI_O_EXCL != 0 { + return __WASI_EEXIST; + } + if o_flags & __WASI_O_DIRECTORY != 0 { + match &child_inode_val.kind { + Kind::Dir { .. } => (), + Kind::Symlink { .. } => { + unimplemented!("Symlinks not yet supported in path_open") + } + _ => return __WASI_ENOTDIR, + } + } + // do logic on child + wasi_try!(state + .fs + .create_fd(fs_rights_base, fs_rights_inheriting, fs_flags, child)) + } else { + // if entry does not exist in parent directory, try to lazily + // load it; possibly creating or truncating it if flags set + let real_opened_file = { + let mut open_options = std::fs::OpenOptions::new(); + let open_options = open_options.read(true).write(true); + let open_options = if o_flags & __WASI_O_CREAT != 0 { + debug!( + "File {} may be created when opened if it does not exist", + &path_string + ); + open_options.create(true) + } else { + open_options + }; + let open_options = if o_flags & __WASI_O_TRUNC != 0 { + debug!("File {} will be truncated when opened", &path_string); + open_options.truncate(true) + } else { + open_options + }; + let real_open_file = + wasi_try!(open_options.open(&file_path).map_err(|_| __WASI_EIO)); + debug!("Opening host file {}", &path_string); + + real_open_file + }; + // record lazily loaded or newly created fd + let new_inode = state.fs.inodes.insert(InodeVal { + stat: __wasi_filestat_t::default(), + is_preopened: false, + name: file_name.clone(), + kind: Kind::File { + handle: WasiFile::HostFile(real_opened_file), + }, + }); + // reborrow to insert entry + if let Kind::Dir { entries, .. } = &mut state.fs.inodes[working_dir.inode].kind { + entries.insert(file_name.clone(), new_inode); + } + let new_fd = wasi_try!(state.fs.create_fd( + fs_rights_base, + fs_rights_inheriting, + fs_flags, + new_inode, + )); + + new_fd + } + } else { + // working_dir did not match on Kind::Dir + return __WASI_ENOTDIR; + }; + + fd_cell.set(out_fd); + + __WASI_ESUCCESS +} + +pub fn path_readlink( + ctx: &mut Ctx, + fd: __wasi_fd_t, + path: WasmPtr, + path_len: u32, + buf: WasmPtr, + buf_len: u32, + bufused: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::path_readlink"); + unimplemented!("wasi::path_readlink") +} +pub fn path_remove_directory( + ctx: &mut Ctx, + fd: __wasi_fd_t, + path: WasmPtr, + path_len: u32, +) -> __wasi_errno_t { + debug!("wasi::path_remove_directory"); + unimplemented!("wasi::path_remove_directory") +} +pub fn path_rename( + ctx: &mut Ctx, + old_fd: __wasi_fd_t, + old_path: WasmPtr, + old_path_len: u32, + new_fd: __wasi_fd_t, + new_path: WasmPtr, + new_path_len: u32, +) -> __wasi_errno_t { + debug!("wasi::path_rename"); + unimplemented!("wasi::path_rename") +} +pub fn path_symlink( + ctx: &mut Ctx, + old_path: WasmPtr, + old_path_len: u32, + fd: __wasi_fd_t, + new_path: WasmPtr, + new_path_len: u32, +) -> __wasi_errno_t { + debug!("wasi::path_symlink"); + unimplemented!("wasi::path_symlink") +} +pub fn path_unlink_file( + ctx: &mut Ctx, + fd: __wasi_fd_t, + path: WasmPtr, + path_len: u32, +) -> __wasi_errno_t { + debug!("wasi::path_unlink_file"); + unimplemented!("wasi::path_unlink_file") +} +pub fn poll_oneoff( + ctx: &mut Ctx, + in_: WasmPtr<__wasi_subscription_t, Array>, + out_: WasmPtr<__wasi_event_t, Array>, + nsubscriptions: u32, + nevents: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::poll_oneoff"); + unimplemented!("wasi::poll_oneoff") +} +pub fn proc_exit(ctx: &mut Ctx, code: __wasi_exitcode_t) -> Result { + debug!("wasi::proc_exit, {}", code); + Err(ExitCode { code }) +} +pub fn proc_raise(ctx: &mut Ctx, sig: __wasi_signal_t) -> __wasi_errno_t { + debug!("wasi::proc_raise"); + unimplemented!("wasi::proc_raise") +} + +/// ### `random_get()` +/// Fill buffer with high-quality random data. This function may be slow and block +/// Inputs: +/// - `void *buf` +/// A pointer to a buffer where the random bytes will be written +/// - `size_t buf_len` +/// The number of bytes that will be written +pub fn random_get(ctx: &mut Ctx, buf: WasmPtr, buf_len: u32) -> __wasi_errno_t { + debug!("wasi::random_get"); + let mut rng = thread_rng(); + let memory = ctx.memory(0); + + let buf = wasi_try!(buf.deref(memory, 0, buf_len)); + + unsafe { + let u8_buffer = &mut *(buf as *const [_] as *mut [_] as *mut [u8]); + thread_rng().fill(u8_buffer); + } + + __WASI_ESUCCESS +} + +/// ### `sched_yield()` +/// Yields execution of the thread +pub fn sched_yield(ctx: &mut Ctx) -> __wasi_errno_t { + debug!("wasi::sched_yield"); + ::std::thread::yield_now(); + __WASI_ESUCCESS +} + +pub fn sock_recv( + ctx: &mut Ctx, + sock: __wasi_fd_t, + ri_data: WasmPtr<__wasi_iovec_t, Array>, + ri_data_len: u32, + ri_flags: __wasi_riflags_t, + ro_datalen: WasmPtr, + ro_flags: WasmPtr<__wasi_roflags_t>, +) -> __wasi_errno_t { + debug!("wasi::sock_recv"); + unimplemented!("wasi::sock_recv") +} +pub fn sock_send( + ctx: &mut Ctx, + sock: __wasi_fd_t, + si_data: WasmPtr<__wasi_ciovec_t, Array>, + si_data_len: u32, + si_flags: __wasi_siflags_t, + so_datalen: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::sock_send"); + unimplemented!("wasi::sock_send") +} +pub fn sock_shutdown(ctx: &mut Ctx, sock: __wasi_fd_t, how: __wasi_sdflags_t) -> __wasi_errno_t { + debug!("wasi::sock_shutdown"); + unimplemented!("wasi::sock_shutdown") +} diff --git a/lib/wasi/src/syscalls/types.rs b/lib/wasi/src/syscalls/types.rs new file mode 100644 index 000000000..636e1858e --- /dev/null +++ b/lib/wasi/src/syscalls/types.rs @@ -0,0 +1,459 @@ +#![allow(non_camel_case_types)] + +use crate::ptr::{Array, WasmPtr}; +use byteorder::{ReadBytesExt, WriteBytesExt, LE}; +use std::fmt; +use std::mem; +use wasmer_runtime_core::types::ValueType; + +pub type __wasi_advice_t = u8; +pub const __WASI_ADVICE_DONTNEED: u8 = 0; +pub const __WASI_ADVICE_NOREUSE: u8 = 1; +pub const __WASI_ADVICE_NORMAL: u8 = 2; +pub const __WASI_ADVICE_RANDOM: u8 = 3; +pub const __WASI_ADVICE_SEQUENTIAL: u8 = 4; +pub const __WASI_ADVICE_WILLNEED: u8 = 5; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(C)] +pub struct __wasi_ciovec_t { + pub buf: WasmPtr, + pub buf_len: u32, +} + +unsafe impl ValueType for __wasi_ciovec_t {} + +pub type __wasi_clockid_t = u32; +pub const __WASI_CLOCK_MONOTONIC: u32 = 0; +pub const __WASI_CLOCK_PROCESS_CPUTIME_ID: u32 = 1; +pub const __WASI_CLOCK_REALTIME: u32 = 2; +pub const __WASI_CLOCK_THREAD_CPUTIME_ID: u32 = 3; + +pub type __wasi_device_t = u64; + +pub type __wasi_dircookie_t = u64; +pub const __WASI_DIRCOOKIE_START: u64 = 0; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(C)] +pub struct __wasi_dirent_t { + pub d_next: __wasi_dircookie_t, + pub d_ino: __wasi_inode_t, + pub d_namlen: u32, + pub d_type: __wasi_filetype_t, +} + +pub type __wasi_errno_t = u16; +pub const __WASI_ESUCCESS: u16 = 0; +pub const __WASI_E2BIG: u16 = 1; +pub const __WASI_EACCES: u16 = 2; +pub const __WASI_EADDRINUSE: u16 = 3; +pub const __WASI_EADDRNOTAVAIL: u16 = 4; +pub const __WASI_EAFNOSUPPORT: u16 = 5; +pub const __WASI_EAGAIN: u16 = 6; +pub const __WASI_EALREADY: u16 = 7; +pub const __WASI_EBADF: u16 = 8; +pub const __WASI_EBADMSG: u16 = 9; +pub const __WASI_EBUSY: u16 = 10; +pub const __WASI_ECANCELED: u16 = 11; +pub const __WASI_ECHILD: u16 = 12; +pub const __WASI_ECONNABORTED: u16 = 13; +pub const __WASI_ECONNREFUSED: u16 = 14; +pub const __WASI_ECONNRESET: u16 = 15; +pub const __WASI_EDEADLK: u16 = 16; +pub const __WASI_EDESTADDRREQ: u16 = 17; +pub const __WASI_EDOM: u16 = 18; +pub const __WASI_EDQUOT: u16 = 19; +pub const __WASI_EEXIST: u16 = 20; +pub const __WASI_EFAULT: u16 = 21; +pub const __WASI_EFBIG: u16 = 22; +pub const __WASI_EHOSTUNREACH: u16 = 23; +pub const __WASI_EIDRM: u16 = 24; +pub const __WASI_EILSEQ: u16 = 25; +pub const __WASI_EINPROGRESS: u16 = 26; +pub const __WASI_EINTR: u16 = 27; +pub const __WASI_EINVAL: u16 = 28; +pub const __WASI_EIO: u16 = 29; +pub const __WASI_EISCONN: u16 = 30; +pub const __WASI_EISDIR: u16 = 31; +pub const __WASI_ELOOP: u16 = 32; +pub const __WASI_EMFILE: u16 = 33; +pub const __WASI_EMLINK: u16 = 34; +pub const __WASI_EMSGSIZE: u16 = 35; +pub const __WASI_EMULTIHOP: u16 = 36; +pub const __WASI_ENAMETOOLONG: u16 = 37; +pub const __WASI_ENETDOWN: u16 = 38; +pub const __WASI_ENETRESET: u16 = 39; +pub const __WASI_ENETUNREACH: u16 = 40; +pub const __WASI_ENFILE: u16 = 41; +pub const __WASI_ENOBUFS: u16 = 42; +pub const __WASI_ENODEV: u16 = 43; +pub const __WASI_ENOENT: u16 = 44; +pub const __WASI_ENOEXEC: u16 = 45; +pub const __WASI_ENOLCK: u16 = 46; +pub const __WASI_ENOLINK: u16 = 47; +pub const __WASI_ENOMEM: u16 = 48; +pub const __WASI_ENOMSG: u16 = 49; +pub const __WASI_ENOPROTOOPT: u16 = 50; +pub const __WASI_ENOSPC: u16 = 51; +pub const __WASI_ENOSYS: u16 = 52; +pub const __WASI_ENOTCONN: u16 = 53; +pub const __WASI_ENOTDIR: u16 = 54; +pub const __WASI_ENOTEMPTY: u16 = 55; +pub const __WASI_ENOTRECOVERABLE: u16 = 56; +pub const __WASI_ENOTSOCK: u16 = 57; +pub const __WASI_ENOTSUP: u16 = 58; +pub const __WASI_ENOTTY: u16 = 59; +pub const __WASI_ENXIO: u16 = 60; +pub const __WASI_EOVERFLOW: u16 = 61; +pub const __WASI_EOWNERDEAD: u16 = 62; +pub const __WASI_EPERM: u16 = 63; +pub const __WASI_EPIPE: u16 = 64; +pub const __WASI_EPROTO: u16 = 65; +pub const __WASI_EPROTONOSUPPORT: u16 = 66; +pub const __WASI_EPROTOTYPE: u16 = 67; +pub const __WASI_ERANGE: u16 = 68; +pub const __WASI_EROFS: u16 = 69; +pub const __WASI_ESPIPE: u16 = 70; +pub const __WASI_ESRCH: u16 = 71; +pub const __WASI_ESTALE: u16 = 72; +pub const __WASI_ETIMEDOUT: u16 = 73; +pub const __WASI_ETXTBSY: u16 = 74; +pub const __WASI_EXDEV: u16 = 75; +pub const __WASI_ENOTCAPABLE: u16 = 76; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(C)] +pub struct __wasi_event_fd_readwrite_t { + pub nbytes: __wasi_filesize_t, + pub flags: __wasi_eventrwflags_t, +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub union __wasi_event_u { + fd_readwrite: __wasi_event_fd_readwrite_t, +} + +#[derive(Copy, Clone)] +pub enum EventEnum { + FdReadWrite { + nbytes: __wasi_filesize_t, + flags: __wasi_eventrwflags_t, + }, +} + +impl EventEnum { + pub fn untagged(self) -> __wasi_event_u { + match self { + EventEnum::FdReadWrite { nbytes, flags } => __wasi_event_u { + fd_readwrite: __wasi_event_fd_readwrite_t { nbytes, flags }, + }, + } + } +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct __wasi_event_t { + pub userdata: __wasi_userdata_t, + pub error: __wasi_errno_t, + pub type_: __wasi_eventtype_t, + pub u: __wasi_event_u, +} + +impl __wasi_event_t { + pub fn tagged(&self) -> Option { + match self.type_ { + __WASI_EVENTTYPE_FD_READ | __WASI_EVENTTYPE_FD_WRITE => Some(EventEnum::FdReadWrite { + nbytes: unsafe { self.u.fd_readwrite.nbytes }, + flags: unsafe { self.u.fd_readwrite.flags }, + }), + _ => None, + } + } +} + +pub type __wasi_eventrwflags_t = u16; +pub const __WASI_EVENT_FD_READWRITE_HANGUP: u16 = 1 << 0; + +pub type __wasi_eventtype_t = u8; +pub const __WASI_EVENTTYPE_CLOCK: u8 = 0; +pub const __WASI_EVENTTYPE_FD_READ: u8 = 1; +pub const __WASI_EVENTTYPE_FD_WRITE: u8 = 2; + +pub type __wasi_exitcode_t = u32; + +pub type __wasi_fd_t = u32; +pub const __WASI_STDIN_FILENO: u32 = 0; +pub const __WASI_STDOUT_FILENO: u32 = 1; +pub const __WASI_STDERR_FILENO: u32 = 2; + +pub type __wasi_fdflags_t = u16; +pub const __WASI_FDFLAG_APPEND: u16 = 1 << 0; +pub const __WASI_FDFLAG_DSYNC: u16 = 1 << 1; +pub const __WASI_FDFLAG_NONBLOCK: u16 = 1 << 2; +pub const __WASI_FDFLAG_RSYNC: u16 = 1 << 3; +pub const __WASI_FDFLAG_SYNC: u16 = 1 << 4; + +pub type __wasi_preopentype_t = u8; +pub const __WASI_PREOPENTYPE_DIR: u8 = 0; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(C)] +pub struct __wasi_prestat_u_dir_t { + pub pr_name_len: u32, +} + +unsafe impl ValueType for __wasi_prestat_u_dir_t {} + +#[derive(Copy, Clone)] +#[repr(C)] +pub union __wasi_prestat_u { + dir: __wasi_prestat_u_dir_t, +} + +impl fmt::Debug for __wasi_prestat_u { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "__wasi_prestat_u") + } +} + +unsafe impl ValueType for __wasi_prestat_u {} + +#[derive(Debug, Copy, Clone)] +#[repr(C)] +pub struct __wasi_prestat_t { + pub pr_type: __wasi_preopentype_t, + pub u: __wasi_prestat_u, +} + +#[derive(Copy, Clone)] +pub enum PrestatEnum { + Dir { pr_name_len: u32 }, +} + +impl PrestatEnum { + pub fn untagged(self) -> __wasi_prestat_u { + match self { + PrestatEnum::Dir { pr_name_len } => __wasi_prestat_u { + dir: __wasi_prestat_u_dir_t { pr_name_len }, + }, + } + } +} + +impl __wasi_prestat_t { + pub fn tagged(&self) -> Option { + match self.pr_type { + __WASI_PREOPENTYPE_DIR => Some(PrestatEnum::Dir { + pr_name_len: unsafe { self.u.dir.pr_name_len }, + }), + _ => None, + } + } +} + +unsafe impl ValueType for __wasi_prestat_t {} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(C)] +pub struct __wasi_fdstat_t { + pub fs_filetype: __wasi_filetype_t, + pub fs_flags: __wasi_fdflags_t, + pub fs_rights_base: __wasi_rights_t, + pub fs_rights_inheriting: __wasi_rights_t, +} + +unsafe impl ValueType for __wasi_fdstat_t {} + +pub type __wasi_filedelta_t = i64; + +pub type __wasi_filesize_t = u64; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] +#[repr(C)] +pub struct __wasi_filestat_t { + pub st_dev: __wasi_device_t, + pub st_ino: __wasi_inode_t, + pub st_filetype: __wasi_filetype_t, + pub st_nlink: __wasi_linkcount_t, + pub st_size: __wasi_filesize_t, + pub st_atim: __wasi_timestamp_t, + pub st_mtim: __wasi_timestamp_t, + pub st_ctim: __wasi_timestamp_t, +} + +unsafe impl ValueType for __wasi_filestat_t {} + +pub type __wasi_filetype_t = u8; +pub const __WASI_FILETYPE_UNKNOWN: u8 = 0; +pub const __WASI_FILETYPE_BLOCK_DEVICE: u8 = 1; +pub const __WASI_FILETYPE_CHARACTER_DEVICE: u8 = 2; +pub const __WASI_FILETYPE_DIRECTORY: u8 = 3; +pub const __WASI_FILETYPE_REGULAR_FILE: u8 = 4; +pub const __WASI_FILETYPE_SOCKET_DGRAM: u8 = 5; +pub const __WASI_FILETYPE_SOCKET_STREAM: u8 = 6; +pub const __WASI_FILETYPE_SYMBOLIC_LINK: u8 = 7; + +pub type __wasi_fstflags_t = u16; +pub const __WASI_FILESTAT_SET_ATIM: u16 = 1 << 0; +pub const __WASI_FILESTAT_SET_ATIM_NOW: u16 = 1 << 1; +pub const __WASI_FILESTAT_SET_MTIM: u16 = 1 << 2; +pub const __WASI_FILESTAT_SET_MTIM_NOW: u16 = 1 << 3; + +pub type __wasi_inode_t = u64; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(C)] +pub struct __wasi_iovec_t { + pub buf: WasmPtr, + pub buf_len: u32, +} + +unsafe impl ValueType for __wasi_iovec_t {} + +pub type __wasi_linkcount_t = u32; + +pub type __wasi_lookupflags_t = u32; +pub const __WASI_LOOKUP_SYMLINK_FOLLOW: u32 = 1 << 0; + +pub type __wasi_oflags_t = u16; +pub const __WASI_O_CREAT: u16 = 1 << 0; +pub const __WASI_O_DIRECTORY: u16 = 1 << 1; +pub const __WASI_O_EXCL: u16 = 1 << 2; +pub const __WASI_O_TRUNC: u16 = 1 << 3; + +pub type __wasi_riflags_t = u16; +pub const __WASI_SOCK_RECV_PEEK: u16 = 1 << 0; +pub const __WASI_SOCK_RECV_WAITALL: u16 = 1 << 1; + +pub type __wasi_rights_t = u64; +pub const __WASI_RIGHT_FD_DATASYNC: u64 = 1 << 0; +pub const __WASI_RIGHT_FD_READ: u64 = 1 << 1; +pub const __WASI_RIGHT_FD_SEEK: u64 = 1 << 2; +pub const __WASI_RIGHT_FD_FDSTAT_SET_FLAGS: u64 = 1 << 3; +pub const __WASI_RIGHT_FD_SYNC: u64 = 1 << 4; +pub const __WASI_RIGHT_FD_TELL: u64 = 1 << 5; +pub const __WASI_RIGHT_FD_WRITE: u64 = 1 << 6; +pub const __WASI_RIGHT_FD_ADVISE: u64 = 1 << 7; +pub const __WASI_RIGHT_FD_ALLOCATE: u64 = 1 << 8; +pub const __WASI_RIGHT_PATH_CREATE_DIRECTORY: u64 = 1 << 9; +pub const __WASI_RIGHT_PATH_CREATE_FILE: u64 = 1 << 10; +pub const __WASI_RIGHT_PATH_LINK_SOURCE: u64 = 1 << 11; +pub const __WASI_RIGHT_PATH_LINK_TARGET: u64 = 1 << 12; +pub const __WASI_RIGHT_PATH_OPEN: u64 = 1 << 13; +pub const __WASI_RIGHT_FD_READDIR: u64 = 1 << 14; +pub const __WASI_RIGHT_PATH_READLINK: u64 = 1 << 15; +pub const __WASI_RIGHT_PATH_RENAME_SOURCE: u64 = 1 << 16; +pub const __WASI_RIGHT_PATH_RENAME_TARGET: u64 = 1 << 17; +pub const __WASI_RIGHT_PATH_FILESTAT_GET: u64 = 1 << 18; +pub const __WASI_RIGHT_PATH_FILESTAT_SET_SIZE: u64 = 1 << 19; +pub const __WASI_RIGHT_PATH_FILESTAT_SET_TIMES: u64 = 1 << 20; +pub const __WASI_RIGHT_FD_FILESTAT_GET: u64 = 1 << 21; +pub const __WASI_RIGHT_FD_FILESTAT_SET_SIZE: u64 = 1 << 22; +pub const __WASI_RIGHT_FD_FILESTAT_SET_TIMES: u64 = 1 << 23; +pub const __WASI_RIGHT_PATH_SYMLINK: u64 = 1 << 24; +pub const __WASI_RIGHT_PATH_UNLINK_FILE: u64 = 1 << 25; +pub const __WASI_RIGHT_PATH_REMOVE_DIRECTORY: u64 = 1 << 26; +pub const __WASI_RIGHT_POLL_FD_READWRITE: u64 = 1 << 27; +pub const __WASI_RIGHT_SOCK_SHUTDOWN: u64 = 1 << 28; + +pub type __wasi_roflags_t = u16; +pub const __WASI_SOCK_RECV_DATA_TRUNCATED: u16 = 1 << 0; + +pub type __wasi_sdflags_t = u8; +pub const __WASI_SHUT_RD: u8 = 1 << 0; +pub const __WASI_SHUT_WR: u8 = 1 << 1; + +pub type __wasi_siflags_t = u16; + +pub type __wasi_signal_t = u8; +pub const __WASI_SIGABRT: u8 = 0; +pub const __WASI_SIGALRM: u8 = 1; +pub const __WASI_SIGBUS: u8 = 2; +pub const __WASI_SIGCHLD: u8 = 3; +pub const __WASI_SIGCONT: u8 = 4; +pub const __WASI_SIGFPE: u8 = 5; +pub const __WASI_SIGHUP: u8 = 6; +pub const __WASI_SIGILL: u8 = 7; +pub const __WASI_SIGINT: u8 = 8; +pub const __WASI_SIGKILL: u8 = 9; +pub const __WASI_SIGPIPE: u8 = 10; +pub const __WASI_SIGQUIT: u8 = 11; +pub const __WASI_SIGSEGV: u8 = 12; +pub const __WASI_SIGSTOP: u8 = 13; +pub const __WASI_SIGSYS: u8 = 14; +pub const __WASI_SIGTERM: u8 = 15; +pub const __WASI_SIGTRAP: u8 = 16; +pub const __WASI_SIGTSTP: u8 = 17; +pub const __WASI_SIGTTIN: u8 = 18; +pub const __WASI_SIGTTOU: u8 = 19; +pub const __WASI_SIGURG: u8 = 20; +pub const __WASI_SIGUSR1: u8 = 21; +pub const __WASI_SIGUSR2: u8 = 22; +pub const __WASI_SIGVTALRM: u8 = 23; +pub const __WASI_SIGXCPU: u8 = 24; +pub const __WASI_SIGXFSZ: u8 = 25; + +pub type __wasi_subclockflags_t = u16; +pub const __WASI_SUBSCRIPTION_CLOCK_ABSTIME: u16 = 1 << 0; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(C)] +pub struct __wasi_subscription_clock_t { + pub userdata: __wasi_userdata_t, + pub clock_id: __wasi_clockid_t, + pub timeout: __wasi_timestamp_t, + pub precision: __wasi_timestamp_t, + pub flags: __wasi_subclockflags_t, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(C)] +pub struct __wasi_subscription_fs_readwrite_t { + pub fd: __wasi_fd_t, +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub union __wasi_subscription_u { + clock: __wasi_subscription_clock_t, + fd_readwrite: __wasi_subscription_fs_readwrite_t, +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct __wasi_subscription_t { + pub userdata: __wasi_userdata_t, + pub type_: __wasi_eventtype_t, + pub u: __wasi_subscription_u, +} + +pub enum SubscriptionEnum { + Clock(__wasi_subscription_clock_t), + FdReadWrite(__wasi_subscription_fs_readwrite_t), +} + +impl __wasi_subscription_t { + pub fn tagged(&self) -> Option { + match self.type_ { + __WASI_EVENTTYPE_CLOCK => Some(SubscriptionEnum::Clock(unsafe { self.u.clock })), + __WASI_EVENTTYPE_FD_READ | __WASI_EVENTTYPE_FD_WRITE => { + Some(SubscriptionEnum::FdReadWrite(unsafe { + self.u.fd_readwrite + })) + } + _ => None, + } + } +} + +pub type __wasi_timestamp_t = u64; + +pub type __wasi_userdata_t = u64; + +pub type __wasi_whence_t = u8; +pub const __WASI_WHENCE_CUR: u8 = 0; +pub const __WASI_WHENCE_END: u8 = 1; +pub const __WASI_WHENCE_SET: u8 = 2; diff --git a/lib/wasi/src/syscalls/unix/mod.rs b/lib/wasi/src/syscalls/unix/mod.rs new file mode 100644 index 000000000..4c9cfada3 --- /dev/null +++ b/lib/wasi/src/syscalls/unix/mod.rs @@ -0,0 +1,59 @@ +use crate::syscalls::types::*; +use libc::{ + clock_getres, clock_gettime, timespec, CLOCK_MONOTONIC, CLOCK_PROCESS_CPUTIME_ID, + CLOCK_REALTIME, CLOCK_THREAD_CPUTIME_ID, +}; +use std::cell::Cell; +use std::mem; + +pub fn platform_clock_res_get( + clock_id: __wasi_clockid_t, + resolution: &Cell<__wasi_timestamp_t>, +) -> __wasi_errno_t { + let unix_clock_id = match clock_id { + __WASI_CLOCK_MONOTONIC => CLOCK_MONOTONIC, + __WASI_CLOCK_PROCESS_CPUTIME_ID => CLOCK_PROCESS_CPUTIME_ID, + __WASI_CLOCK_REALTIME => CLOCK_REALTIME, + __WASI_CLOCK_THREAD_CPUTIME_ID => CLOCK_THREAD_CPUTIME_ID, + _ => return __WASI_EINVAL, + }; + + let (output, timespec_out) = unsafe { + let mut timespec_out: timespec = mem::uninitialized(); + (clock_getres(unix_clock_id, &mut timespec_out), timespec_out) + }; + + resolution.set(timespec_out.tv_nsec as __wasi_timestamp_t); + + // TODO: map output of clock_getres to __wasi_errno_t + __WASI_ESUCCESS +} + +pub fn platform_clock_time_get( + clock_id: __wasi_clockid_t, + precision: __wasi_timestamp_t, + time: &Cell<__wasi_timestamp_t>, +) -> __wasi_errno_t { + let unix_clock_id = match clock_id { + __WASI_CLOCK_MONOTONIC => CLOCK_MONOTONIC, + __WASI_CLOCK_PROCESS_CPUTIME_ID => CLOCK_PROCESS_CPUTIME_ID, + __WASI_CLOCK_REALTIME => CLOCK_REALTIME, + __WASI_CLOCK_THREAD_CPUTIME_ID => CLOCK_THREAD_CPUTIME_ID, + _ => return __WASI_EINVAL, + }; + + let (output, timespec_out) = unsafe { + let mut timespec_out: timespec = mem::uninitialized(); + ( + clock_gettime(unix_clock_id, &mut timespec_out), + timespec_out, + ) + }; + + // TODO: adjust output by precision... + + time.set(timespec_out.tv_nsec as __wasi_timestamp_t); + + // TODO: map output of clock_gettime to __wasi_errno_t + __WASI_ESUCCESS +} diff --git a/lib/wasi/src/syscalls/windows.rs b/lib/wasi/src/syscalls/windows.rs new file mode 100644 index 000000000..6273695d3 --- /dev/null +++ b/lib/wasi/src/syscalls/windows.rs @@ -0,0 +1,17 @@ +use crate::syscalls::types::*; +use std::cell::Cell; + +pub fn platform_clock_res_get( + clock_id: __wasi_clockid_t, + resolution: &Cell<__wasi_timestamp_t>, +) -> __wasi_errno_t { + __WASI_EINVAL +} + +pub fn platform_clock_time_get( + clock_id: __wasi_clockid_t, + precision: __wasi_timestamp_t, + time: &Cell<__wasi_timestamp_t>, +) -> __wasi_errno_t { + unimplemented!() +} diff --git a/lib/wasi/src/utils.rs b/lib/wasi/src/utils.rs new file mode 100644 index 000000000..4c3680be2 --- /dev/null +++ b/lib/wasi/src/utils.rs @@ -0,0 +1,15 @@ +use wasmer_runtime_core::module::Module; + +/// Check if a provided module is compiled with WASI support +pub fn is_wasi_module(module: &Module) -> bool { + for (_, import_name) in &module.info().imported_functions { + let namespace = module + .info() + .namespace_table + .get(import_name.namespace_index); + if namespace == "wasi_unstable" { + return true; + } + } + false +} diff --git a/lib/win-exception-handler/Cargo.toml b/lib/win-exception-handler/Cargo.toml index 18855212b..fe9342feb 100644 --- a/lib/win-exception-handler/Cargo.toml +++ b/lib/win-exception-handler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-win-exception-handler" -version = "0.2.0" +version = "0.4.1" description = "Wasmer runtime exception handling for Windows" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -8,7 +8,7 @@ repository = "https://github.com/wasmerio/wasmer" edition = "2018" [target.'cfg(windows)'.dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.2.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } winapi = { version = "0.3", features = ["winbase", "errhandlingapi", "minwindef", "minwinbase", "winnt"] } libc = "0.2.49" diff --git a/lib/win-exception-handler/README.md b/lib/win-exception-handler/README.md new file mode 100644 index 000000000..9a32e1753 --- /dev/null +++ b/lib/win-exception-handler/README.md @@ -0,0 +1,31 @@ +

+ + Wasmer logo + +

+ +

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

+ +# Wasmer Windows Exception Handler + +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 provides an API for exception handling on Windows. diff --git a/lib/win-exception-handler/build.rs b/lib/win-exception-handler/build.rs index 7376c6329..1ed351206 100644 --- a/lib/win-exception-handler/build.rs +++ b/lib/win-exception-handler/build.rs @@ -1,8 +1,7 @@ -use cmake::Config; - fn main() { #[cfg(target_os = "windows")] { + use cmake::Config; let project_name = "exception_handling"; let dst = Config::new(project_name).build(); println!("cargo:rustc-link-search=native={}", dst.display()); diff --git a/lib/win-exception-handler/exception_handling/exception_handling.c b/lib/win-exception-handler/exception_handling/exception_handling.c index eb45b204d..c3ecbab1d 100644 --- a/lib/win-exception-handler/exception_handling/exception_handling.c +++ b/lib/win-exception-handler/exception_handling/exception_handling.c @@ -5,6 +5,7 @@ #define CALL_FIRST 1 __declspec(thread) jmp_buf jmpBuf; +__declspec(thread) DWORD caughtExceptionCode; __declspec(thread) PVOID caughtExceptionAddress; __declspec(thread) DWORD64 caughtInstructionPointer; __declspec(thread) PVOID savedStackPointer; @@ -25,6 +26,7 @@ static LONG WINAPI exceptionHandler(struct _EXCEPTION_POINTERS *ExceptionInfo) { EXCEPTION_RECORD* pExceptionRecord = ExceptionInfo->ExceptionRecord; PCONTEXT pCONTEXT = ExceptionInfo->ContextRecord; + caughtExceptionCode = pExceptionRecord->ExceptionCode; caughtExceptionAddress = pExceptionRecord->ExceptionAddress; caughtInstructionPointer = pCONTEXT->Rip; if (alreadyHandlingException == TRUE) { @@ -61,26 +63,27 @@ uint8_t callProtected(trampoline_t trampoline, } // jmp jmp jmp! - int signum = setjmp(jmpBuf); - if (signum == 0) { + int status = setjmp(jmpBuf); + if (status == 0) // 0 means the original call + { // 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; + out_result->exception_address = 0; + out_result->instruction_pointer = 0; removeExceptionHandler(); return TRUE; } - out_result->code = (uint64_t)signum; - out_result->exceptionAddress = (uint64_t)caughtExceptionAddress; - out_result->instructionPointer = caughtInstructionPointer; + out_result->code = (uint64_t)caughtExceptionCode; + out_result->exception_address = (uint64_t)caughtExceptionAddress; + out_result->instruction_pointer = caughtInstructionPointer; caughtExceptionAddress = 0; caughtInstructionPointer = 0; removeExceptionHandler(); return FALSE; -} +} \ No newline at end of file diff --git a/lib/win-exception-handler/exception_handling/exception_handling.h b/lib/win-exception-handler/exception_handling/exception_handling.h index cd5472149..f00227600 100644 --- a/lib/win-exception-handler/exception_handling/exception_handling.h +++ b/lib/win-exception-handler/exception_handling/exception_handling.h @@ -10,8 +10,8 @@ typedef void(*trampoline_t)(struct wasmer_instance_context_t*, const struct fun struct call_protected_result_t { uint64_t code; - uint64_t exceptionAddress; - uint64_t instructionPointer; + uint64_t exception_address; + uint64_t instruction_pointer; }; uint8_t callProtected( diff --git a/lib/win-exception-handler/src/exception_handling.rs b/lib/win-exception-handler/src/exception_handling.rs index 04c3c7f2e..c448392b0 100644 --- a/lib/win-exception-handler/src/exception_handling.rs +++ b/lib/win-exception-handler/src/exception_handling.rs @@ -1,14 +1,14 @@ -use std::ffi::c_void; +use std::ptr::NonNull; use wasmer_runtime_core::vm::{Ctx, Func}; -type Trampoline = unsafe extern "C" fn(*mut Ctx, *const Func, *const u64, *mut u64) -> c_void; +type Trampoline = unsafe extern "C" fn(*mut Ctx, NonNull, *const u64, *mut u64); type CallProtectedResult = Result<(), CallProtectedData>; #[repr(C)] pub struct CallProtectedData { pub code: u64, - pub exceptionAddress: u64, - pub instructionPointer: u64, + pub exception_address: u64, + pub instruction_pointer: u64, } extern "C" { @@ -16,7 +16,7 @@ extern "C" { pub fn __call_protected( trampoline: Trampoline, ctx: *mut Ctx, - func: *const Func, + func: NonNull, param_vec: *const u64, return_vec: *mut u64, out_result: *mut CallProtectedData, @@ -26,14 +26,14 @@ extern "C" { pub fn _call_protected( trampoline: Trampoline, ctx: *mut Ctx, - func: *const Func, + func: NonNull, param_vec: *const u64, return_vec: *mut u64, ) -> CallProtectedResult { let mut out_result = CallProtectedData { code: 0, - exceptionAddress: 0, - instructionPointer: 0, + exception_address: 0, + instruction_pointer: 0, }; let result = unsafe { __call_protected( diff --git a/lib/win-exception-handler/src/lib.rs b/lib/win-exception-handler/src/lib.rs index bc4a142b5..5329e1cc0 100644 --- a/lib/win-exception-handler/src/lib.rs +++ b/lib/win-exception-handler/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + #[cfg(windows)] mod exception_handling; diff --git a/media/wizard_logo.ico b/media/wizard_logo.ico new file mode 100644 index 000000000..664b0d7a1 Binary files /dev/null and b/media/wizard_logo.ico differ diff --git a/media/wizard_logo_2.bmp b/media/wizard_logo_2.bmp new file mode 100644 index 000000000..959d76829 Binary files /dev/null and b/media/wizard_logo_2.bmp differ diff --git a/media/wizard_logo_small.bmp b/media/wizard_logo_small.bmp new file mode 100644 index 000000000..9ca204fca Binary files /dev/null and b/media/wizard_logo_small.bmp differ diff --git a/scripts/install_lib_sodium.sh b/scripts/install_lib_sodium.sh new file mode 100755 index 000000000..51e46c57c --- /dev/null +++ b/scripts/install_lib_sodium.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +curl -O https://download.libsodium.org/libsodium/releases/libsodium-1.0.17.tar.gz +tar xf libsodium-1.0.17.tar.gz +cd libsodium-1.0.17/ +./configure +make && make check +sudo make install diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index e36e99cb6..14386d1b1 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -1,18 +1,49 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + extern crate structopt; use std::env; -use std::fs::File; +use std::fs::{read_to_string, File}; use std::io; use std::io::Read; use std::path::PathBuf; use std::process::exit; +use std::str::FromStr; +use hashbrown::HashMap; use structopt::StructOpt; -use wasmer::webassembly::InstanceABI; use wasmer::*; -use wasmer_emscripten; -use wasmer_runtime::cache::{Cache as BaseCache, FileSystemCache, WasmHash}; +use wasmer_clif_backend::CraneliftCompiler; +#[cfg(feature = "backend:llvm")] +use wasmer_llvm_backend::LLVMCompiler; +use wasmer_runtime::{ + cache::{Cache as BaseCache, FileSystemCache, WasmHash, WASMER_VERSION_HASH}, + error::RuntimeError, + Func, Value, +}; +use wasmer_runtime_core::{ + self, + backend::{Compiler, CompilerConfig}, +}; +#[cfg(feature = "backend:singlepass")] +use wasmer_singlepass_backend::SinglePassCompiler; +#[cfg(feature = "wasi")] +use wasmer_wasi; + +// stub module to make conditional compilation happy +#[cfg(not(feature = "wasi"))] +mod wasmer_wasi { + use wasmer_runtime_core::{import::ImportObject, module::Module}; + + pub fn is_wasi_module(_module: &Module) -> bool { + false + } + + pub fn generate_import_object(_args: Vec>, _envs: Vec>) -> ImportObject { + unimplemented!() + } +} #[derive(Debug, StructOpt)] #[structopt(name = "wasmer", about = "Wasm execution runtime.")] @@ -26,6 +57,10 @@ enum CLIOptions { #[structopt(name = "cache")] Cache(Cache), + /// Validate a Web Assembly binary + #[structopt(name = "validate")] + Validate(Validate), + /// Update wasmer to the latest version #[structopt(name = "self-update")] SelfUpdate, @@ -41,20 +76,88 @@ struct Run { #[structopt(parse(from_os_str))] path: PathBuf, + // Disable the cache + #[structopt( + long = "backend", + default_value = "cranelift", + raw(possible_values = "Backend::variants()", case_insensitive = "true") + )] + backend: Backend, + + /// Emscripten symbol map + #[structopt(long = "em-symbol-map", parse(from_os_str), group = "emscripten")] + em_symbol_map: Option, + + /// Begin execution at the specified symbol + #[structopt(long = "em-entrypoint", group = "emscripten")] + em_entrypoint: Option, + + /// WASI pre-opened directory + #[structopt(long = "dir", multiple = true, group = "wasi")] + pre_opened_directories: Vec, + + #[structopt(long = "command-name", hidden = true)] + command_name: Option, + /// Application arguments #[structopt(name = "--", raw(multiple = "true"))] args: Vec, } +#[allow(dead_code)] +#[derive(Debug)] +enum Backend { + Cranelift, + Singlepass, + LLVM, +} + +impl Backend { + pub fn variants() -> &'static [&'static str] { + &[ + "cranelift", + #[cfg(feature = "backend:singlepass")] + "singlepass", + #[cfg(feature = "backend:llvm")] + "llvm", + ] + } +} + +impl FromStr for Backend { + type Err = String; + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "singlepass" => Ok(Backend::Singlepass), + "cranelift" => Ok(Backend::Cranelift), + "llvm" => Ok(Backend::LLVM), + // "llvm" => Err( + // "The LLVM backend option is not enabled by default due to binary size constraints" + // .to_string(), + // ), + _ => Err(format!("The backend {} doesn't exist", s)), + } + } +} + #[derive(Debug, StructOpt)] enum Cache { + /// Clear the cache #[structopt(name = "clean")] Clean, + /// Display the location of the cache #[structopt(name = "dir")] Dir, } +#[derive(Debug, StructOpt)] +struct Validate { + /// Input file + #[structopt(parse(from_os_str))] + path: PathBuf, +} + /// Read the contents of a file fn read_file_contents(path: &PathBuf) -> Result, io::Error> { let mut buffer: Vec = Vec::new(); @@ -72,6 +175,7 @@ fn get_cache_dir() -> PathBuf { // We use a temporal directory for saving cache files let mut temp_dir = env::temp_dir(); temp_dir.push("wasmer"); + temp_dir.push(WASMER_VERSION_HASH); temp_dir } } @@ -95,11 +199,65 @@ fn execute_wasm(options: &Run) -> Result<(), String> { ) })?; + let em_symbol_map = if let Some(em_symbol_map_path) = options.em_symbol_map.clone() { + let em_symbol_map_content: String = read_to_string(&em_symbol_map_path) + .map_err(|err| { + format!( + "Can't read symbol map file {}: {}", + em_symbol_map_path.as_os_str().to_string_lossy(), + err, + ) + })? + .to_owned(); + let mut em_symbol_map = HashMap::new(); + for line in em_symbol_map_content.lines() { + let mut split = line.split(':'); + let num_str = if let Some(ns) = split.next() { + ns + } else { + return Err(format!( + "Can't parse symbol map (expected each entry to be of the form: `0:func_name`)" + )); + }; + let num: u32 = num_str.parse::().map_err(|err| { + format!( + "Failed to parse {} as a number in symbol map: {}", + num_str, err + ) + })?; + let name_str: String = if let Some(name_str) = split.next() { + name_str + } else { + return Err(format!( + "Can't parse symbol map (expected each entry to be of the form: `0:func_name`)" + )); + } + .to_owned(); + + em_symbol_map.insert(num, name_str); + } + Some(em_symbol_map) + } else { + None + }; + if !utils::is_wasm_binary(&wasm_binary) { wasm_binary = wabt::wat2wasm(wasm_binary) .map_err(|e| format!("Can't convert from wast to wasm: {:?}", e))?; } + let compiler: Box = match options.backend { + #[cfg(feature = "backend:singlepass")] + Backend::Singlepass => Box::new(SinglePassCompiler::new()), + #[cfg(not(feature = "backend:singlepass"))] + Backend::Singlepass => return Err("The singlepass backend is not enabled".to_string()), + Backend::Cranelift => Box::new(CraneliftCompiler::new()), + #[cfg(feature = "backend:llvm")] + Backend::LLVM => Box::new(LLVMCompiler::new()), + #[cfg(not(feature = "backend:llvm"))] + Backend::LLVM => return Err("the llvm backend is not enabled".to_string()), + }; + let module = if !disable_cache { // If we have cache enabled @@ -125,46 +283,109 @@ fn execute_wasm(options: &Run) -> Result<(), String> { module } Err(_) => { - let module = webassembly::compile(&wasm_binary[..]) - .map_err(|e| format!("Can't compile module: {:?}", e))?; + let module = webassembly::compile_with_config_with( + &wasm_binary[..], + CompilerConfig { + symbol_map: em_symbol_map, + }, + &*compiler, + ) + .map_err(|e| format!("Can't compile module: {:?}", e))?; + // We try to save the module into a cache file + cache.store(hash, module.clone()).unwrap_or_default(); - // 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))? + webassembly::compile_with_config_with( + &wasm_binary[..], + CompilerConfig { + symbol_map: em_symbol_map, + }, + &*compiler, + ) + .map_err(|e| format!("Can't compile module: {:?}", e))? }; - let (_abi, import_object, _em_globals) = if wasmer_emscripten::is_emscripten_module(&module) { + // TODO: refactor this + if wasmer_emscripten::is_emscripten_module(&module) { let mut emscripten_globals = wasmer_emscripten::EmscriptenGlobals::new(&module); - ( - InstanceABI::Emscripten, - wasmer_emscripten::generate_emscripten_env(&mut emscripten_globals), - Some(emscripten_globals), // TODO Em Globals is here to extend, lifetime, find better solution + let import_object = wasmer_emscripten::generate_emscripten_env(&mut emscripten_globals); + let mut instance = module + .instantiate(&import_object) + .map_err(|e| format!("Can't instantiate module: {:?}", e))?; + + wasmer_emscripten::run_emscripten_instance( + &module, + &mut instance, + if let Some(cn) = &options.command_name { + cn + } else { + options.path.to_str().unwrap() + }, + options.args.iter().map(|arg| arg.as_str()).collect(), + options.em_entrypoint.clone(), ) + .map_err(|e| format!("{:?}", e))?; } else { - ( - InstanceABI::None, - wasmer_runtime_core::import::ImportObject::new(), - None, - ) - }; + if cfg!(feature = "wasi") && wasmer_wasi::is_wasi_module(&module) { + let import_object = wasmer_wasi::generate_import_object( + if let Some(cn) = &options.command_name { + [cn.clone()] + } else { + [options.path.to_str().unwrap().to_owned()] + } + .iter() + .chain(options.args.iter()) + .cloned() + .map(|arg| arg.into_bytes()) + .collect(), + env::vars() + .map(|(k, v)| format!("{}={}", k, v).into_bytes()) + .collect(), + options.pre_opened_directories.clone(), + ); - let mut instance = module - .instantiate(&import_object) - .map_err(|e| format!("Can't instantiate module: {:?}", e))?; + let instance = module + .instantiate(&import_object) + .map_err(|e| format!("Can't instantiate module: {:?}", e))?; - webassembly::run_instance( - &module, - &mut instance, - options.path.to_str().unwrap(), - options.args.iter().map(|arg| arg.as_str()).collect(), - ) - .map_err(|e| format!("{:?}", e))?; + let start: Func<(), ()> = instance.func("_start").map_err(|e| format!("{:?}", e))?; + + let result = start.call(); + + if let Err(ref err) = result { + match err { + RuntimeError::Trap { msg } => panic!("wasm trap occured: {}", msg), + RuntimeError::Error { data } => { + if let Some(error_code) = data.downcast_ref::() { + std::process::exit(error_code.code as i32) + } + } + } + panic!("error: {:?}", err) + } + } else { + let import_object = wasmer_runtime_core::import::ImportObject::new(); + let instance = module + .instantiate(&import_object) + .map_err(|e| format!("Can't instantiate module: {:?}", e))?; + + let args: Vec = options + .args + .iter() + .map(|arg| arg.as_str()) + .map(|x| Value::I32(x.parse().unwrap())) + .collect(); + instance + .dyn_func("main") + .map_err(|e| format!("{:?}", e))? + .call(&args) + .map_err(|e| format!("{:?}", e))?; + } + } Ok(()) } @@ -179,6 +400,42 @@ fn run(options: Run) { } } +fn validate_wasm(validate: Validate) -> Result<(), String> { + let wasm_path = validate.path; + let wasm_path_as_str = wasm_path.to_str().unwrap(); + + let wasm_binary: Vec = read_file_contents(&wasm_path).map_err(|err| { + format!( + "Can't read the file {}: {}", + wasm_path.as_os_str().to_string_lossy(), + err + ) + })?; + + if !utils::is_wasm_binary(&wasm_binary) { + return Err(format!( + "Cannot recognize \"{}\" as a WASM binary", + wasm_path_as_str, + )); + } + + wasmer_runtime_core::validate_and_report_errors(&wasm_binary) + .map_err(|err| format!("Validation failed: {}", err))?; + + Ok(()) +} + +/// Runs logic for the `validate` subcommand +fn validate(validate: Validate) { + match validate_wasm(validate) { + Err(message) => { + eprintln!("Error: {}", message); + exit(-1); + } + _ => (), + } +} + fn main() { let options = CLIOptions::from_args(); match options { @@ -194,13 +451,18 @@ fn main() { 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"); + if cache_dir.exists() { + fs::remove_dir_all(cache_dir.clone()).expect("Can't remove cache dir"); + } + fs::create_dir_all(cache_dir.clone()).expect("Can't create cache dir"); } Cache::Dir => { println!("{}", get_cache_dir().to_string_lossy()); } }, + CLIOptions::Validate(validate_options) => { + validate(validate_options); + } #[cfg(target_os = "windows")] CLIOptions::Cache(_) => { println!("Caching is disabled for Windows."); diff --git a/src/installer/wasmer.iss b/src/installer/wasmer.iss index f844eb4da..16a396593 100644 --- a/src/installer/wasmer.iss +++ b/src/installer/wasmer.iss @@ -1,6 +1,6 @@ [Setup] AppName=Wasmer -AppVersion=1.5 +AppVersion=0.4.0 DefaultDirName={pf}\Wasmer DefaultGroupName=Wasmer Compression=lzma2 @@ -9,9 +9,17 @@ OutputDir=.\ DisableProgramGroupPage=yes ChangesEnvironment=yes OutputBaseFilename=WasmerInstaller +WizardImageFile=..\..\media\wizard_logo_2.bmp +WizardSmallImageFile=..\..\media\wizard_logo_small.bmp +SetupIconFile=..\..\media\wizard_logo.ico +DisableWelcomePage=no [Files] Source: "..\..\target\release\wasmer.exe"; DestDir: "{app}\bin" +Source: "..\..\wapm-cli\target\release\wapm.exe"; DestDir: "{app}\bin" + +[Dirs] +Name: "{%USERPROFILE}\.wasmer" [Code] const EnvironmentKey = 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'; @@ -61,11 +69,17 @@ end; procedure CurStepChanged(CurStep: TSetupStep); begin if CurStep = ssPostInstall - then EnvAddPath(ExpandConstant('{app}') +'\bin'); + then begin + EnvAddPath(ExpandConstant('{app}') +'\bin'); + EnvAddPath(ExpandConstant('{%USERPROFILE}') +'\globals\wapm_packages\.bin'); + end end; procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); begin if CurUninstallStep = usPostUninstall - then EnvRemovePath(ExpandConstant('{app}') +'\bin'); + then begin + EnvRemovePath(ExpandConstant('{app}') +'\bin'); + EnvAddPath(ExpandConstant('{%USERPROFILE}') +'\globals\wapm_packages\.bin'); + end end; \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 25af8304f..78d6dae28 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + #[macro_use] extern crate wasmer_runtime_core; // extern crate wasmer_emscripten; diff --git a/src/webassembly.rs b/src/webassembly.rs index bbe50b4d6..0df8b59e8 100644 --- a/src/webassembly.rs +++ b/src/webassembly.rs @@ -1,11 +1,6 @@ use std::panic; -use wasmer_runtime::{ - self as runtime, - error::{CallResult, Result}, - ImportObject, Instance, Module, -}; - -use wasmer_emscripten::{is_emscripten_module, run_emscripten_instance}; +pub use wasmer_runtime::compile_with_config_with; +use wasmer_runtime::{self as runtime, error::Result, ImportObject, Instance, Module}; pub struct ResultObject { /// A webassembly::Module object representing the compiled WebAssembly module. @@ -19,6 +14,7 @@ pub struct ResultObject { #[derive(PartialEq)] pub enum InstanceABI { Emscripten, + WASI, None, } @@ -74,20 +70,3 @@ pub fn compile(buffer_source: &[u8]) -> Result { let module = runtime::compile(buffer_source)?; Ok(module) } - -/// Performs common instance operations needed when an instance is first run -/// including data setup, handling arguments and calling a main function -pub fn run_instance( - module: &Module, - instance: &mut Instance, - path: &str, - args: Vec<&str>, -) -> CallResult<()> { - if is_emscripten_module(module) { - run_emscripten_instance(module, instance, path, args)?; - } else { - instance.call("main", &[])?; - }; - - Ok(()) -} diff --git a/update_version_numbers.sh b/update_version_numbers.sh new file mode 100755 index 000000000..db448940e --- /dev/null +++ b/update_version_numbers.sh @@ -0,0 +1,17 @@ +PREVIOUS_VERSION='0.4.0' +NEXT_VERSION='0.4.1' + +# quick hack +fd Cargo.toml --exec sed -i '' "s/version = \"$PREVIOUS_VERSION\"/version = \"$NEXT_VERSION\"/" +echo "manually check changes to Cargo.toml" + +# Order to upload packages in +## runtime-core +## win-exception-handler +## clif-backend +## llvm-backend +## singlepass-backend +## emscripten +## wasi +## runtime +## runtime-c-api diff --git a/wapm-cli b/wapm-cli new file mode 160000 index 000000000..8286d0a4b --- /dev/null +++ b/wapm-cli @@ -0,0 +1 @@ +Subproject commit 8286d0a4bcd771c2f5e622b40543143e25e096df