Merge branch 'master' into feat-c-api-update-wasm-h

This commit is contained in:
Ivan Enderlin
2020-10-16 10:18:46 +02:00
46 changed files with 1727 additions and 555 deletions

View File

@@ -1,4 +1,4 @@
[target.x86_64-unknown-linux-gnu] [target.'cfg(target_os = "linux")']
rustflags = [ rustflags = [
"-C", "link-arg=-Wl,-E" "-C", "link-arg=-Wl,-E"
] ]

View File

@@ -35,7 +35,7 @@ jobs:
# that are needed during the build process. Additionally, this works # that are needed during the build process. Additionally, this works
# around a bug in the 'cache' action that causes directories outside of # around a bug in the 'cache' action that causes directories outside of
# the workspace dir to be saved/restored incorrectly. # the workspace dir to be saved/restored incorrectly.
run: echo "::set-env name=CARGO_HOME::$(pwd)/.cargo_home" run: echo "CARGO_HOME=$(pwd)/.cargo_home" >> $GITHUB_ENV
- name: Cache - name: Cache
uses: actions/cache@master uses: actions/cache@master
with: with:
@@ -56,8 +56,8 @@ jobs:
curl --proto '=https' --tlsv1.2 -sSf https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/clang+llvm-10.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz -L -o llvm.tar.xz curl --proto '=https' --tlsv1.2 -sSf https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/clang+llvm-10.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz -L -o llvm.tar.xz
mkdir -p /opt/llvm-10 mkdir -p /opt/llvm-10
tar xf llvm.tar.xz --strip-components=1 -C /opt/llvm-10 tar xf llvm.tar.xz --strip-components=1 -C /opt/llvm-10
echo ::add-path::/opt/llvm-10/bin echo '/opt/llvm-10/bin' >> $GITHUB_PATH
echo ::set-env name=LLVM_SYS_100_PREFIX::/opt/llvm-10 echo 'name=LLVM_SYS_100_PREFIX=/opt/llvm-10' >> $GITHUB_ENV
- name: Install Python - name: Install Python
uses: actions/setup-python@v2 uses: actions/setup-python@v2
with: with:

View File

@@ -29,8 +29,8 @@ jobs:
curl --proto '=https' --tlsv1.2 -sSf https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/clang+llvm-10.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz -L -o llvm.tar.xz curl --proto '=https' --tlsv1.2 -sSf https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/clang+llvm-10.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz -L -o llvm.tar.xz
mkdir -p /opt/llvm-10 mkdir -p /opt/llvm-10
tar xf llvm.tar.xz --strip-components=1 -C /opt/llvm-10 tar xf llvm.tar.xz --strip-components=1 -C /opt/llvm-10
echo ::add-path::/opt/llvm-10/bin echo '/opt/llvm-10/bin' >> $GITHUB_PATH
echo ::set-env name=LLVM_SYS_100_PREFIX::/opt/llvm-10 echo 'LLVM_SYS_100_PREFIX=/opt/llvm-10' >> $GITHUB_ENV
- name: Generate Coverage Report - name: Generate Coverage Report
run: | run: |
cargo install cargo-tarpaulin cargo install cargo-tarpaulin

View File

@@ -81,7 +81,7 @@ jobs:
# that are needed during the build process. Additionally, this works # that are needed during the build process. Additionally, this works
# around a bug in the 'cache' action that causes directories outside of # around a bug in the 'cache' action that causes directories outside of
# the workspace dir to be saved/restored incorrectly. # the workspace dir to be saved/restored incorrectly.
run: echo "::set-env name=CARGO_HOME::$(pwd)/.cargo_home" run: echo "CARGO_HOME=$(pwd)/.cargo_home" >> $GITHUB_ENV
- name: Cache - name: Cache
uses: actions/cache@master uses: actions/cache@master
with: with:
@@ -103,13 +103,13 @@ jobs:
# key: cargo-sccache-bin-${{ env.CARGO_SCCACHE_VERSION }} # key: cargo-sccache-bin-${{ env.CARGO_SCCACHE_VERSION }}
# - name: Install sccache # - name: Install sccache
# run: | # run: |
# echo "::add-path::${{ runner.tool_cache }}/cargo-sccache/bin" # echo "${{ runner.tool_cache }}/cargo-sccache/bin" >> $GITHUB_PATH
# cargo install sccache --version ${{ env.CARGO_SCCACHE_VERSION }} --root ${{ runner.tool_cache }}/cargo-sccache # cargo install sccache --version ${{ env.CARGO_SCCACHE_VERSION }} --root ${{ runner.tool_cache }}/cargo-sccache
# - name: Start sccache # - name: Start sccache
# run: | # run: |
# ${{ runner.tool_cache }}/cargo-sccache/bin/sccache --start-server # ${{ runner.tool_cache }}/cargo-sccache/bin/sccache --start-server
# ${{ runner.tool_cache }}/cargo-sccache/bin/sscache -s # ${{ runner.tool_cache }}/cargo-sccache/bin/sscache -s
# echo "::set-env name=RUSTC_WRAPPER::${{ runner.tool_cache }}/cargo-sccache/bin/sccache" # echo "RUSTC_WRAPPER=${{ runner.tool_cache }}/cargo-sccache/bin/sccache" >> $GITHUB_ENV
- name: Install LLVM (Windows) - name: Install LLVM (Windows)
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
shell: cmd shell: cmd
@@ -118,17 +118,17 @@ jobs:
# run: | # run: |
# curl --proto '=https' --tlsv1.2 -sSf https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/LLVM-10.0.0-win64.exe -L -o llvm-installer.exe # curl --proto '=https' --tlsv1.2 -sSf https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/LLVM-10.0.0-win64.exe -L -o llvm-installer.exe
# 7z x llvm-installer.exe -oC:/llvm-10 # 7z x llvm-installer.exe -oC:/llvm-10
# echo ::add-path::C:/llvm-10/bin # echo C:/llvm-10/bin >> $GITHUB_PATH
# echo ::set-env name=LLVM_SYS_100_PREFIX::C:/llvm-10 # echo "LLVM_SYS_100_PREFIX=C:/llvm-10" >> $GITHUB_ENV
# echo ::set-env name=LIBCLANG_PATH::C:/llvm-10/bin/libclang.dll # echo "LIBCLANG_PATH=C:/llvm-10/bin/libclang.dll" >> $GITHUB_ENV
- name: Install LLVM (Unix) - name: Install LLVM (Unix)
if: matrix.os != 'windows-latest' if: matrix.os != 'windows-latest'
run: | run: |
curl --proto '=https' --tlsv1.2 -sSf ${{ matrix.llvm_url }} -L -o llvm.tar.xz curl --proto '=https' --tlsv1.2 -sSf ${{ matrix.llvm_url }} -L -o llvm.tar.xz
mkdir -p ${{ env.LLVM_DIR }} mkdir -p ${{ env.LLVM_DIR }}
tar xf llvm.tar.xz --strip-components=1 -C ${{ env.LLVM_DIR }} tar xf llvm.tar.xz --strip-components=1 -C ${{ env.LLVM_DIR }}
echo "::add-path::${{ env.LLVM_DIR }}/bin" echo "${{ env.LLVM_DIR }}/bin" >> $GITHUB_PATH
echo "::set-env name=LLVM_SYS_100_PREFIX::${{ env.LLVM_DIR }}" echo "LLVM_SYS_100_PREFIX=${{ env.LLVM_DIR }}" >> $GITHUB_ENV
env: env:
LLVM_DIR: ${{ github.workspace }}/llvm-10 LLVM_DIR: ${{ github.workspace }}/llvm-10
- name: Set up dependencies for Mac OS - name: Set up dependencies for Mac OS
@@ -150,19 +150,25 @@ jobs:
run: | run: |
make build-wapm make build-wapm
if: needs.setup.outputs.DOING_RELEASE == '1' if: needs.setup.outputs.DOING_RELEASE == '1'
- name: Run integration tests (Windows) - name: Package Wasmer for integration tests
shell: cmd run: make package-without-wapm-for-integration-tests
run: | if: needs.setup.outputs.DOING_RELEASE != '1'
call refreshenv
make test-integration
if: matrix.run_integration_tests && matrix.os == 'windows-latest'
- name: Run integration tests (Unix)
run: make test-integration
if: matrix.run_integration_tests && matrix.os != 'windows-latest'
- name: Package Wasmer - name: Package Wasmer
run: | run: |
make package make package
if: needs.setup.outputs.DOING_RELEASE == '1' if: needs.setup.outputs.DOING_RELEASE == '1'
- name: Run integration tests (Windows)
shell: cmd
run: |
call refreshenv
set WASMER_DIR=%CD%\package
make test-integration
if: matrix.run_integration_tests && matrix.os == 'windows-latest'
- name: Run integration tests (Unix)
run: |
export WASMER_DIR=`pwd`/package
make test-integration
if: matrix.run_integration_tests && matrix.os != 'windows-latest'
- name: Upload Artifacts - name: Upload Artifacts
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
if: needs.setup.outputs.DOING_RELEASE == '1' if: needs.setup.outputs.DOING_RELEASE == '1'
@@ -242,7 +248,7 @@ jobs:
path: ${{ runner.tool_cache }}/cargo-audit path: ${{ runner.tool_cache }}/cargo-audit
key: cargo-audit-bin-${{ env.CARGO_AUDIT_VERSION }} key: cargo-audit-bin-${{ env.CARGO_AUDIT_VERSION }}
- run: | - run: |
echo "::add-path::${{ runner.tool_cache }}/cargo-audit/bin" echo "${{ runner.tool_cache }}/cargo-audit/bin" >> $GITHUB_PATH
- run: | - run: |
cargo install cargo-audit --version ${{ env.CARGO_AUDIT_VERSION }} --root ${{ runner.tool_cache }}/cargo-audit cargo install cargo-audit --version ${{ env.CARGO_AUDIT_VERSION }} --root ${{ runner.tool_cache }}/cargo-audit
cargo audit cargo audit

View File

@@ -1,15 +1,15 @@
[cranelift_coverage] [cranelift_coverage]
features = "cranelift,singlepass,llvm,test-no-traps,test-cranelift" features = "cranelift,singlepass,llvm,coverage,test-cranelift,test-jit"
examples = ["early-exit", "engine-jit", "engine-native", "engine-headless", "cross-compilation", "compiler-cranelift", "exported-function", "wasi"] examples = ["early-exit", "engine-jit", "engine-native", "engine-headless", "cross-compilation", "compiler-cranelift", "exported-function", "wasi"]
release = true release = true
[llvm_coverage] [llvm_coverage]
features = "cranelift,singlepass,llvm,test-no-traps,test-llvm" features = "cranelift,singlepass,llvm,coverage,test-llvm,test-jit"
examples = ["compiler-llvm"] examples = ["compiler-llvm"]
release = true release = true
[singlepass_coverage] [singlepass_coverage]
features = "cranelift,singlepass,llvm,test-no-traps,test-singlepass" features = "cranelift,singlepass,llvm,coverage,test-singlepass,test-jit"
examples = ["compiler-singlepass"] examples = ["compiler-singlepass"]
release = true release = true

View File

@@ -1,85 +1,123 @@
# Changelog # Changelog
*The format is based on [Keep a Changelog].*
[Keep a Changelog]: http://keepachangelog.com/en/1.0.0/
## **[Unreleased]** ## **[Unreleased]**
- [#1699](https://github.com/wasmerio/wasmer/pull/1699) Update `wasm.h` to its latest version. - [#1699](https://github.com/wasmerio/wasmer/pull/1699) Update `wasm.h` to its latest version.
- [#1685](https://github.com/wasmerio/wasmer/pull/1685) Implement `wasm_exporttype_delete` in the Wasm C API. - [#1685](https://github.com/wasmerio/wasmer/pull/1685) Implement `wasm_exporttype_delete` in the Wasm C API.
### Added
- [#1715](https://github.com/wasmerio/wasmer/pull/1715) Register errors from `wasm_module_serialize` in the Wasm C API.
- [#1709](https://github.com/wasmerio/wasmer/pull/1709) Implement `wasm_module_name` and `wasm_module_set_name` in the Wasm(er) C API.
- [#1700](https://github.com/wasmerio/wasmer/pull/1700) Implement `wasm_externtype_copy` in the Wasm C API. - [#1700](https://github.com/wasmerio/wasmer/pull/1700) Implement `wasm_externtype_copy` in the Wasm C API.
### Fixed
- [#1718](https://github.com/wasmerio/wasmer/pull/1718) Fix panic in the API in some situations when the memory's min bound was greater than the memory's max bound.
## 1.0.0-alpha4 - 2020-10-08 ## 1.0.0-alpha4 - 2020-10-08
- [#1691](https://github.com/wasmerio/wasmer/pull/1691) Bump minimum supported Rust version to 1.46.0 ### Added
- [#1690](https://github.com/wasmerio/wasmer/pull/1690) Fix `wasm_memorytype_limits` where `min` and `max` represents pages, not bytes. Additionally, fixes the max limit sentinel value.
- [#1682](https://github.com/wasmerio/wasmer/pull/1682) Improve error reporting when making a memory with invalid settings.
- [#1635](https://github.com/wasmerio/wasmer/pull/1635) Implement `wat2wasm` in the Wasm C API. - [#1635](https://github.com/wasmerio/wasmer/pull/1635) Implement `wat2wasm` in the Wasm C API.
- [#1636](https://github.com/wasmerio/wasmer/pull/1636) Implement `wasm_module_validate` in the Wasm C API. - [#1636](https://github.com/wasmerio/wasmer/pull/1636) Implement `wasm_module_validate` in the Wasm C API.
- [#1657](https://github.com/wasmerio/wasmer/pull/1657) Implement `wasm_trap_t` and `wasm_frame_t` for Wasm C API; add examples in Rust and C of exiting early with a host function.
### Fixed
- [#1690](https://github.com/wasmerio/wasmer/pull/1690) Fix `wasm_memorytype_limits` where `min` and `max` represents pages, not bytes. Additionally, fixes the max limit sentinel value.
- [#1671](https://github.com/wasmerio/wasmer/pull/1671) Fix probestack firing inappropriately, and sometimes over/under allocating stack. - [#1671](https://github.com/wasmerio/wasmer/pull/1671) Fix probestack firing inappropriately, and sometimes over/under allocating stack.
- [#1660](https://github.com/wasmerio/wasmer/pull/1660) Fix issue preventing map-dir aliases starting with `/` from working properly. - [#1660](https://github.com/wasmerio/wasmer/pull/1660) Fix issue preventing map-dir aliases starting with `/` from working properly.
- [#1657](https://github.com/wasmerio/wasmer/pull/1657) Implement `wasm_trap_t` and `wasm_frame_t` for Wasm C API; add examples in Rust and C of exiting early with a host function.
### Changed
- [#1682](https://github.com/wasmerio/wasmer/pull/1682) Improve error reporting when making a memory with invalid settings.
- [#1691](https://github.com/wasmerio/wasmer/pull/1691) Bump minimum supported Rust version to 1.46.0
- [#1645](https://github.com/wasmerio/wasmer/pull/1645) Move the install script to https://github.com/wasmerio/wasmer-install - [#1645](https://github.com/wasmerio/wasmer/pull/1645) Move the install script to https://github.com/wasmerio/wasmer-install
## 1.0.0-alpha3 - 2020-09-14 ## 1.0.0-alpha3 - 2020-09-14
### Fixed
- [#1620](https://github.com/wasmerio/wasmer/pull/1620) Fix bug causing the Wapm binary to not be packaged with the release - [#1620](https://github.com/wasmerio/wasmer/pull/1620) Fix bug causing the Wapm binary to not be packaged with the release
- [#1619](https://github.com/wasmerio/wasmer/pull/1619) Improve error message in engine-native when C compiler is missing - [#1619](https://github.com/wasmerio/wasmer/pull/1619) Improve error message in engine-native when C compiler is missing
## 1.0.0-alpha02.0 - 2020-09-11 ## 1.0.0-alpha02.0 - 2020-09-11
### Added
- [#1566](https://github.com/wasmerio/wasmer/pull/1566) Add support for opening special Unix files to the WASI FS
### Fixed
- [#1602](https://github.com/wasmerio/wasmer/pull/1602) Fix panic when calling host functions with negative numbers in certain situations - [#1602](https://github.com/wasmerio/wasmer/pull/1602) Fix panic when calling host functions with negative numbers in certain situations
- [#1590](https://github.com/wasmerio/wasmer/pull/1590) Fix soundness issue in API of vm::Global - [#1590](https://github.com/wasmerio/wasmer/pull/1590) Fix soundness issue in API of vm::Global
- [#1566](https://github.com/wasmerio/wasmer/pull/1566) Add support for opening special Unix files to the WASI FS
## TODO: 1.0.0-alpha01.0 ## TODO: 1.0.0-alpha01.0
- Wasmer refactor lands - Wasmer refactor lands
## TODO: 17... ## 0.17.1 - 2020-06-24
### Changed
- [#1439](https://github.com/wasmerio/wasmer/pull/1439) Move `wasmer-interface-types` into its own repository
### Fixed
- [#1554](https://github.com/wasmerio/wasmer/pull/1554) Update supported stable Rust version to 1.45.2. - [#1554](https://github.com/wasmerio/wasmer/pull/1554) Update supported stable Rust version to 1.45.2.
- [#1552](https://github.com/wasmerio/wasmer/pull/1552) Disable `sigint` handler by default. - [#1552](https://github.com/wasmerio/wasmer/pull/1552) Disable `sigint` handler by default.
## 0.17.1 - 2020-06-24
- [#1439](https://github.com/wasmerio/wasmer/pull/1439) Move `wasmer-interface-types` into its own repository
## 0.17.0 - 2020-05-11 ## 0.17.0 - 2020-05-11
- [#1401](https://github.com/wasmerio/wasmer/pull/1401) Make breaking change to `RuntimeError`: `RuntimeError` is now more explicit about its possible error values allowing for better insight into why a call into Wasm failed. ### Added
- [#1382](https://github.com/wasmerio/wasmer/pull/1382) Refactored test infranstructure (part 2)
- [#1380](https://github.com/wasmerio/wasmer/pull/1380) Refactored test infranstructure (part 1)
- [#1357](https://github.com/wasmerio/wasmer/pull/1357) Refactored bin commands into separate files
- [#1331](https://github.com/wasmerio/wasmer/pull/1331) Implement the `record` type and instrutions for WIT - [#1331](https://github.com/wasmerio/wasmer/pull/1331) Implement the `record` type and instrutions for WIT
- [#1345](https://github.com/wasmerio/wasmer/pull/1345) Adding ARM testing in Azure Pipelines - [#1345](https://github.com/wasmerio/wasmer/pull/1345) Adding ARM testing in Azure Pipelines
- [#1335](https://github.com/wasmerio/wasmer/pull/1335) Change mutability of `memory` to `const` in `wasmer_memory_data_length` in the C API
- [#1329](https://github.com/wasmerio/wasmer/pull/1329) New numbers and strings instructions for WIT - [#1329](https://github.com/wasmerio/wasmer/pull/1329) New numbers and strings instructions for WIT
- [#1332](https://github.com/wasmerio/wasmer/pull/1332) Add option to `CompilerConfig` to force compiler IR verification off even when `debug_assertions` are enabled. This can be used to make debug builds faster, which may be important if you're creating a library that wraps Wasmer and depend on the speed of debug builds. - [#1285](https://github.com/wasmerio/wasmer/pull/1285) Greatly improve errors in `wasmer-interface-types`
- [#1320](https://github.com/wasmerio/wasmer/pull/1320) Change `custom_sections` field in `ModuleInfo` to be more standards compliant by allowing multiple custom sections with the same name. To get the old behavior with the new API, you can add `.last().unwrap()` to accesses. For example, `module_info.custom_sections["custom_section_name"].last().unwrap()`. - [#1303](https://github.com/wasmerio/wasmer/pull/1303) NaN canonicalization for singlepass backend.
- [#1313](https://github.com/wasmerio/wasmer/pull/1313) Add new high-level public API through `wasmer` crate. Includes many updates including: - [#1313](https://github.com/wasmerio/wasmer/pull/1313) Add new high-level public API through `wasmer` crate. Includes many updates including:
- Minor improvement: `imports!` macro now handles no trailing comma as well as a trailing comma in namespaces and between namespaces. - Minor improvement: `imports!` macro now handles no trailing comma as well as a trailing comma in namespaces and between namespaces.
- New methods on `Module`: `exports`, `imports`, and `custom_sections`. - New methods on `Module`: `exports`, `imports`, and `custom_sections`.
- New way to get exports from an instance with `let func_name: Func<i32, i64> = instance.exports.get("func_name");`. - New way to get exports from an instance with `let func_name: Func<i32, i64> = instance.exports.get("func_name");`.
- Improved `Table` APIs including `set` which now allows setting functions directly. TODO: update this more if `Table::get` gets made public in this PR - Improved `Table` APIs including `set` which now allows setting functions directly. TODO: update this more if `Table::get` gets made public in this PR
- TODO: finish the list of changes here - TODO: finish the list of changes here
- [#1303](https://github.com/wasmerio/wasmer/pull/1303) NaN canonicalization for singlepass backend.
- [#1292](https://github.com/wasmerio/wasmer/pull/1292) Experimental Support for Android (x86_64 and AArch64)
- [#1305](https://github.com/wasmerio/wasmer/pull/1305) Handle panics from DynamicFunc. - [#1305](https://github.com/wasmerio/wasmer/pull/1305) Handle panics from DynamicFunc.
- [#1301](https://github.com/wasmerio/wasmer/pull/1301) Update supported stable Rust version to 1.41.1.
- [#1300](https://github.com/wasmerio/wasmer/pull/1300) Add support for multiple versions of WASI tests: wasitests now test all versions of WASI. - [#1300](https://github.com/wasmerio/wasmer/pull/1300) Add support for multiple versions of WASI tests: wasitests now test all versions of WASI.
- [#1285](https://github.com/wasmerio/wasmer/pull/1285) Greatly improve errors in `wasmer-interface-types` - [#1292](https://github.com/wasmerio/wasmer/pull/1292) Experimental Support for Android (x86_64 and AArch64)
### Fixed
- [#1283](https://github.com/wasmerio/wasmer/pull/1283) Workaround for floating point arguments and return values in `DynamicFunc`s. - [#1283](https://github.com/wasmerio/wasmer/pull/1283) Workaround for floating point arguments and return values in `DynamicFunc`s.
### Changed
- [#1401](https://github.com/wasmerio/wasmer/pull/1401) Make breaking change to `RuntimeError`: `RuntimeError` is now more explicit about its possible error values allowing for better insight into why a call into Wasm failed.
- [#1382](https://github.com/wasmerio/wasmer/pull/1382) Refactored test infranstructure (part 2)
- [#1380](https://github.com/wasmerio/wasmer/pull/1380) Refactored test infranstructure (part 1)
- [#1357](https://github.com/wasmerio/wasmer/pull/1357) Refactored bin commands into separate files
- [#1335](https://github.com/wasmerio/wasmer/pull/1335) Change mutability of `memory` to `const` in `wasmer_memory_data_length` in the C API
- [#1332](https://github.com/wasmerio/wasmer/pull/1332) Add option to `CompilerConfig` to force compiler IR verification off even when `debug_assertions` are enabled. This can be used to make debug builds faster, which may be important if you're creating a library that wraps Wasmer and depend on the speed of debug builds.
- [#1320](https://github.com/wasmerio/wasmer/pull/1320) Change `custom_sections` field in `ModuleInfo` to be more standards compliant by allowing multiple custom sections with the same name. To get the old behavior with the new API, you can add `.last().unwrap()` to accesses. For example, `module_info.custom_sections["custom_section_name"].last().unwrap()`.
- [#1301](https://github.com/wasmerio/wasmer/pull/1301) Update supported stable Rust version to 1.41.1.
## 0.16.2 - 2020-03-11 ## 0.16.2 - 2020-03-11
### Fixed
- [#1294](https://github.com/wasmerio/wasmer/pull/1294) Fix bug related to system calls in WASI that rely on reading from WasmPtrs as arrays of length 0. `WasmPtr` will now succeed on length 0 arrays again. - [#1294](https://github.com/wasmerio/wasmer/pull/1294) Fix bug related to system calls in WASI that rely on reading from WasmPtrs as arrays of length 0. `WasmPtr` will now succeed on length 0 arrays again.
## 0.16.1 - 2020-03-11 ## 0.16.1 - 2020-03-11
### Fixed
- [#1291](https://github.com/wasmerio/wasmer/pull/1291) Fix installation packaging script to package the `wax` command. - [#1291](https://github.com/wasmerio/wasmer/pull/1291) Fix installation packaging script to package the `wax` command.
## 0.16.0 - 2020-03-11 ## 0.16.0 - 2020-03-11
### Added
- [#1286](https://github.com/wasmerio/wasmer/pull/1286) Updated Windows Wasmer icons. Add wax - [#1286](https://github.com/wasmerio/wasmer/pull/1286) Updated Windows Wasmer icons. Add wax
- [#1284](https://github.com/wasmerio/wasmer/pull/1284) Implement string and memory instructions in `wasmer-interface-types` - [#1284](https://github.com/wasmerio/wasmer/pull/1284) Implement string and memory instructions in `wasmer-interface-types`
### Fixed
- [#1272](https://github.com/wasmerio/wasmer/pull/1272) Fix off-by-one error bug when accessing memory with a `WasmPtr` that contains the last valid byte of memory. Also changes the behavior of `WasmPtr<T, Array>` with a length of 0 and `WasmPtr<T>` where `std::mem::size_of::<T>()` is 0 to always return `None` - [#1272](https://github.com/wasmerio/wasmer/pull/1272) Fix off-by-one error bug when accessing memory with a `WasmPtr` that contains the last valid byte of memory. Also changes the behavior of `WasmPtr<T, Array>` with a length of 0 and `WasmPtr<T>` where `std::mem::size_of::<T>()` is 0 to always return `None`
## 0.15.0 - 2020-03-04 ## 0.15.0 - 2020-03-04

1
Cargo.lock generated
View File

@@ -2215,6 +2215,7 @@ dependencies = [
"fern", "fern",
"log", "log",
"structopt", "structopt",
"tempfile",
"wasmer", "wasmer",
"wasmer-cache", "wasmer-cache",
"wasmer-compiler", "wasmer-compiler",

View File

@@ -129,9 +129,18 @@ test-llvm = [
"llvm", "llvm",
] ]
# Disable trap asserts in the WAST tests. This is useful for running the tests in a test-native = [
# context where signal handling is a problem, such as tarpaulin for code coverage. "native",
test-no-traps = ["wasmer-wast/test-no-traps"] "test-generator/test-native",
]
test-jit = [
"jit",
"test-generator/test-jit",
]
# Specifies that we're running in coverage testing mode. This disables tests
# that raise signals because that interferes with tarpaulin.
coverage = []
[[bench]] [[bench]]
name = "static_and_dynamic_functions" name = "static_and_dynamic_functions"

View File

@@ -8,11 +8,13 @@ else
UNAME_S := UNAME_S :=
endif endif
compilers := # Which compilers we build. These have dependencies that may not on the system.
compilers := cranelift
# Which engines we test. We always build all engines.
engines := jit
ifeq ($(ARCH), x86_64) ifeq ($(ARCH), x86_64)
# In X64, Cranelift is enabled
compilers += cranelift
# LLVM could be enabled if not in Windows # LLVM could be enabled if not in Windows
ifneq ($(OS), Windows_NT) ifneq ($(OS), Windows_NT)
# Singlepass doesn't work yet on Windows # Singlepass doesn't work yet on Windows
@@ -29,10 +31,14 @@ ifeq ($(ARCH), x86_64)
compilers += llvm compilers += llvm
endif endif
endif endif
# Native engine doesn't work yet on Windows
engines += native
endif endif
endif endif
compilers := $(filter-out ,$(compilers)) compilers := $(filter-out ,$(compilers))
engines := $(filter-out ,$(engines))
ifneq ($(OS), Windows_NT) ifneq ($(OS), Windows_NT)
bold := $(shell tput bold) bold := $(shell tput bold)
@@ -94,16 +100,26 @@ build-capi-llvm:
# Testing # # Testing #
########### ###########
test: $(foreach compiler,$(compilers),test-$(compiler)) test-packages test-examples test-deprecated test: $(foreach engine,$(engines),$(foreach compiler,$(compilers),test-$(compiler)-$(engine))) test-packages test-examples test-deprecated
test-singlepass: # Singlepass and native engine don't work together, this rule does nothing.
cargo test --release $(compiler_features) --features "test-singlepass" test-singlepass-native:
@:
test-cranelift: test-singlepass-jit:
cargo test --release $(compiler_features) --features "test-cranelift" cargo test --release $(compiler_features) --features "test-singlepass test-jit"
test-llvm: test-cranelift-native:
cargo test --release $(compiler_features) --features "test-llvm" cargo test --release $(compiler_features) --features "test-cranelift test-native"
test-cranelift-jit:
cargo test --release $(compiler_features) --features "test-cranelift test-jit"
test-llvm-native:
cargo test --release $(compiler_features) --features "test-llvm test-native"
test-llvm-jit:
cargo test --release $(compiler_features) --features "test-llvm test-jit"
test-packages: test-packages:
cargo test -p wasmer --release cargo test -p wasmer --release
@@ -175,6 +191,7 @@ package-capi:
mkdir -p "package/lib" mkdir -p "package/lib"
cp lib/c-api/wasmer.h* package/include cp lib/c-api/wasmer.h* package/include
cp lib/c-api/wasmer_wasm.h* package/include cp lib/c-api/wasmer_wasm.h* package/include
cp lib/c-api/wasm.h* package/include
cp lib/c-api/doc/deprecated/index.md package/include/README.md cp lib/c-api/doc/deprecated/index.md package/include/README.md
ifeq ($(OS), Windows_NT) ifeq ($(OS), Windows_NT)
cp target/release/wasmer_c_api.dll package/lib cp target/release/wasmer_c_api.dll package/lib
@@ -213,6 +230,9 @@ else
cp ./wasmer.tar.gz ./dist/$(shell ./scripts/binary-name.sh) cp ./wasmer.tar.gz ./dist/$(shell ./scripts/binary-name.sh)
endif endif
# command for simulating installing Wasmer without wapm.
package-without-wapm-for-integration-tests: package-wasmer package-capi
################# #################
# Miscellaneous # # Miscellaneous #
################# #################

View File

@@ -147,7 +147,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
#[test] #[test]
#[cfg(not(windows))] #[cfg(not(any(windows, target_arch = "aarch64")))]
fn test_engine_headless() -> Result<(), Box<dyn std::error::Error>> { fn test_engine_headless() -> Result<(), Box<dyn std::error::Error>> {
main() main()
} }

View File

@@ -87,6 +87,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
#[test] #[test]
#[cfg(not(target_arch = "aarch64"))]
fn test_engine_native() -> Result<(), Box<dyn std::error::Error>> { fn test_engine_native() -> Result<(), Box<dyn std::error::Error>> {
main() main()
} }

View File

@@ -3,7 +3,7 @@
missing_docs, missing_docs,
trivial_numeric_casts, trivial_numeric_casts,
unused_extern_crates, unused_extern_crates,
intra_doc_link_resolution_failure broken_intra_doc_links
)] )]
#![warn(unused_import_braces)] #![warn(unused_import_braces)]
#![cfg_attr( #![cfg_attr(

View File

@@ -1,5 +1,4 @@
use crate::{MemoryType, Pages, TableType}; use crate::{MemoryType, Pages, TableType};
use more_asserts::assert_ge;
use std::cmp::min; use std::cmp::min;
use std::sync::Arc; use std::sync::Arc;
use target_lexicon::{OperatingSystem, PointerWidth}; use target_lexicon::{OperatingSystem, PointerWidth};
@@ -67,7 +66,6 @@ impl BaseTunables for Tunables {
// If the module doesn't declare an explicit maximum treat it as 4GiB. // If the module doesn't declare an explicit maximum treat it as 4GiB.
let maximum = memory.maximum.unwrap_or_else(Pages::max_value); let maximum = memory.maximum.unwrap_or_else(Pages::max_value);
if maximum <= self.static_memory_bound { if maximum <= self.static_memory_bound {
assert_ge!(self.static_memory_bound, memory.minimum);
MemoryStyle::Static { MemoryStyle::Static {
bound: self.static_memory_bound, bound: self.static_memory_bound,
offset_guard_size: self.static_memory_offset_guard_size, offset_guard_size: self.static_memory_offset_guard_size,

View File

@@ -361,6 +361,8 @@ fn exclude_items_from_wasm_c_api(builder: Builder) -> Builder {
builder builder
.exclude_item("wasi_config_arg") .exclude_item("wasi_config_arg")
.exclude_item("wasi_config_env") .exclude_item("wasi_config_env")
.exclude_item("wasi_config_mapdir")
.exclude_item("wasi_config_preopen_dir")
.exclude_item("wasi_config_inherit_stderr") .exclude_item("wasi_config_inherit_stderr")
.exclude_item("wasi_config_inherit_stdin") .exclude_item("wasi_config_inherit_stdin")
.exclude_item("wasi_config_inherit_stdout") .exclude_item("wasi_config_inherit_stdout")
@@ -378,10 +380,12 @@ fn exclude_items_from_wasm_c_api(builder: Builder) -> Builder {
.exclude_item("wasi_get_start_function") .exclude_item("wasi_get_start_function")
.exclude_item("wasi_get_wasi_version") .exclude_item("wasi_get_wasi_version")
.exclude_item("wasi_version_t") .exclude_item("wasi_version_t")
.exclude_item("wasm_instance_get_vmctx_ptr")
.exclude_item("wasmer_compiler_t")
.exclude_item("wasmer_engine_t")
.exclude_item("wasm_config_set_compiler") .exclude_item("wasm_config_set_compiler")
.exclude_item("wasm_config_set_engine") .exclude_item("wasm_config_set_engine")
.exclude_item("wasm_instance_get_vmctx_ptr")
.exclude_item("wasm_module_name")
.exclude_item("wasm_module_set_name")
.exclude_item("wasmer_compiler_t")
.exclude_item("wasmer_engine_t")
.exclude_item("wat2wasm") .exclude_item("wat2wasm")
} }

View File

@@ -108,7 +108,10 @@ pub unsafe extern "C" fn wasm_module_serialize(
) { ) {
let byte_vec = match module.inner.serialize() { let byte_vec = match module.inner.serialize() {
Ok(byte_vec) => byte_vec, Ok(byte_vec) => byte_vec,
Err(_) => return, Err(err) => {
crate::error::update_last_error(err);
return;
}
}; };
*out_ptr = byte_vec.into(); *out_ptr = byte_vec.into();
} }

View File

@@ -83,10 +83,8 @@ pub extern "C" fn wasm_valtype_new(kind: wasm_valkind_t) -> Option<Box<wasm_valt
pub unsafe extern "C" fn wasm_valtype_delete(_valtype: Option<Box<wasm_valtype_t>>) {} pub unsafe extern "C" fn wasm_valtype_delete(_valtype: Option<Box<wasm_valtype_t>>) {}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn wasm_valtype_kind(valtype: *const wasm_valtype_t) -> wasm_valkind_t { pub unsafe extern "C" fn wasm_valtype_kind(valtype: Option<&wasm_valtype_t>) -> wasm_valkind_t {
if valtype.is_null() { valtype
// TODO: handle error .expect("`wasm_valtype_kind: argument is a null pointer")
panic!("wasm_valtype_kind: argument is null pointer"); .valkind as wasm_valkind_t
}
return (*valtype).valkind as wasm_valkind_t;
} }

View File

@@ -75,6 +75,63 @@ pub unsafe extern "C" fn wasi_config_arg(config: &mut wasi_config_t, arg: *const
config.state_builder.arg(arg_bytes); config.state_builder.arg(arg_bytes);
} }
#[no_mangle]
pub unsafe extern "C" fn wasi_config_preopen_dir(
config: &mut wasi_config_t,
dir: *const c_char,
) -> bool {
let dir_cstr = CStr::from_ptr(dir);
let dir_bytes = dir_cstr.to_bytes();
let dir_str = match std::str::from_utf8(dir_bytes) {
Ok(dir_str) => dir_str,
Err(e) => {
update_last_error(e);
return false;
}
};
if let Err(e) = config.state_builder.preopen_dir(dir_str) {
update_last_error(e);
return false;
}
true
}
#[no_mangle]
pub unsafe extern "C" fn wasi_config_mapdir(
config: &mut wasi_config_t,
alias: *const c_char,
dir: *const c_char,
) -> bool {
let alias_cstr = CStr::from_ptr(alias);
let alias_bytes = alias_cstr.to_bytes();
let alias_str = match std::str::from_utf8(alias_bytes) {
Ok(alias_str) => alias_str,
Err(e) => {
update_last_error(e);
return false;
}
};
let dir_cstr = CStr::from_ptr(dir);
let dir_bytes = dir_cstr.to_bytes();
let dir_str = match std::str::from_utf8(dir_bytes) {
Ok(dir_str) => dir_str,
Err(e) => {
update_last_error(e);
return false;
}
};
if let Err(e) = config.state_builder.map_dir(alias_str, dir_str) {
update_last_error(e);
return false;
}
true
}
#[no_mangle] #[no_mangle]
pub extern "C" fn wasi_config_inherit_stdout(config: &mut wasi_config_t) { pub extern "C" fn wasi_config_inherit_stdout(config: &mut wasi_config_t) {
config.inherit_stdout = true; config.inherit_stdout = true;

View File

@@ -1,9 +1,42 @@
//! Wasmer-specific extensions to the Wasm C API. //! Wasmer-specific extensions to the Wasm C API.
use crate::wasm_c_api::instance::wasm_instance_t; use super::instance::wasm_instance_t;
use super::module::wasm_module_t;
use super::types::wasm_name_t;
use std::ffi::c_void; use std::ffi::c_void;
use std::str;
use std::sync::Arc;
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn wasm_instance_get_vmctx_ptr(instance: &wasm_instance_t) -> *mut c_void { pub unsafe extern "C" fn wasm_instance_get_vmctx_ptr(instance: &wasm_instance_t) -> *mut c_void {
instance.inner.vmctx_ptr() as _ instance.inner.vmctx_ptr() as _
} }
#[no_mangle]
pub unsafe extern "C" fn wasm_module_name(module: &wasm_module_t, out: &mut wasm_name_t) {
let name = match module.inner.name() {
Some(name) => name,
None => return,
};
*out = name.as_bytes().to_vec().into();
}
#[no_mangle]
pub unsafe extern "C" fn wasm_module_set_name(
module: &mut wasm_module_t,
name: &wasm_name_t,
) -> bool {
let name = match name.into_slice() {
Some(name) => match str::from_utf8(name) {
Ok(name) => name,
Err(_) => return false, // not ideal!
},
None => return false,
};
match Arc::get_mut(&mut module.inner) {
Some(module) => module.set_name(name),
None => false,
}
}

View File

@@ -62,6 +62,66 @@ int main(int argc, const char *argv[]) {
wasm_memorytype_delete(memtype3); wasm_memorytype_delete(memtype3);
wasm_memory_delete(memory3); wasm_memory_delete(memory3);
// =====================
wasm_limits_t limits4 = {
.min = 0x7FFFFFFF,
.max = 0x7FFFFFFF,
};
own wasm_memorytype_t* memtype4 = wasm_memorytype_new(&limits4);
own wasm_memory_t* memory4 = wasm_memory_new(store, memtype4);
assert(memory4 == NULL);
error = get_wasmer_error();
printf("Found error string: %s\n", error);
assert(0 == strcmp("The minimum requested (2147483647 pages) memory is greater than the maximum allowed memory (65536 pages)", error));
free(error);
wasm_memorytype_delete(memtype4);
// =====================
wasm_limits_t limits5 = {
.min = 0x7FFFFFFF,
.max = 0x0FFFFFFF,
};
own wasm_memorytype_t* memtype5 = wasm_memorytype_new(&limits5);
own wasm_memory_t* memory5 = wasm_memory_new(store, memtype5);
assert(memory5 == NULL);
error = get_wasmer_error();
printf("Found error string: %s\n", error);
assert(0 == strcmp("The minimum requested (2147483647 pages) memory is greater than the maximum allowed memory (65536 pages)", error));
free(error);
wasm_memorytype_delete(memtype5);
// =====================
wasm_limits_t limits6 = {
.min = 15,
.max = 10,
};
own wasm_memorytype_t* memtype6 = wasm_memorytype_new(&limits6);
own wasm_memory_t* memory6 = wasm_memory_new(store, memtype6);
assert(memory6 == NULL);
error = get_wasmer_error();
printf("Found error string: %s\n", error);
assert(0 == strcmp("The memory is invalid because the maximum (10 pages) is less than the minimum (15 pages)", error));
free(error);
wasm_memorytype_delete(memtype6);
// =====================
wasm_limits_t limits7 = {
.min = 0x7FFFFFFF,
.max = 10,
};
own wasm_memorytype_t* memtype7 = wasm_memorytype_new(&limits7);
own wasm_memory_t* memory7 = wasm_memory_new(store, memtype7);
assert(memory7 == NULL);
error = get_wasmer_error();
printf("Found error string: %s\n", error);
assert(0 == strcmp("The minimum requested (2147483647 pages) memory is greater than the maximum allowed memory (65536 pages)", error));
free(error);
wasm_memorytype_delete(memtype7);
printf("Shutting down...\n"); printf("Shutting down...\n");
wasm_store_delete(store); wasm_store_delete(store);
wasm_engine_delete(engine); wasm_engine_delete(engine);

View File

@@ -101,10 +101,18 @@ void wasi_config_inherit_stdin(wasi_config_t *config);
void wasi_config_inherit_stdout(wasi_config_t *config); void wasi_config_inherit_stdout(wasi_config_t *config);
#endif #endif
#if defined(WASMER_WASI_ENABLED)
bool wasi_config_mapdir(wasi_config_t *config, const char *alias, const char *dir);
#endif
#if defined(WASMER_WASI_ENABLED) #if defined(WASMER_WASI_ENABLED)
wasi_config_t *wasi_config_new(const char *program_name); wasi_config_t *wasi_config_new(const char *program_name);
#endif #endif
#if defined(WASMER_WASI_ENABLED)
bool wasi_config_preopen_dir(wasi_config_t *config, const char *dir);
#endif
#if defined(WASMER_WASI_ENABLED) #if defined(WASMER_WASI_ENABLED)
void wasi_env_delete(wasi_env_t *_state); void wasi_env_delete(wasi_env_t *_state);
#endif #endif
@@ -156,6 +164,10 @@ void wasm_config_set_engine(wasm_config_t *config, wasmer_engine_t engine);
void *wasm_instance_get_vmctx_ptr(const wasm_instance_t *instance); void *wasm_instance_get_vmctx_ptr(const wasm_instance_t *instance);
void wasm_module_name(const wasm_module_t *module, wasm_name_t *out);
bool wasm_module_set_name(wasm_module_t *module, const wasm_name_t *name);
/** /**
* Gets the length in bytes of the last error if any. * Gets the length in bytes of the last error if any.
* *

View File

@@ -45,6 +45,7 @@ cfg-if = "0.1"
# For debug feature # For debug feature
fern = { version = "0.6", features = ["colored"], optional = true } fern = { version = "0.6", features = ["colored"], optional = true }
log = { version = "0.4", optional = true } log = { version = "0.4", optional = true }
tempfile = "3"
[features] [features]
# Don't add the compiler features in default, please add them on the Makefile # Don't add the compiler features in default, please add them on the Makefile

View File

@@ -1,4 +1,6 @@
use anyhow::Result; use anyhow::Result;
#[cfg(all(feature = "object-file", feature = "compiler"))]
use wasmer_cli::commands::CreateExe;
#[cfg(feature = "wast")] #[cfg(feature = "wast")]
use wasmer_cli::commands::Wast; use wasmer_cli::commands::Wast;
use wasmer_cli::commands::{Cache, Compile, Config, Inspect, Run, SelfUpdate, Validate}; use wasmer_cli::commands::{Cache, Compile, Config, Inspect, Run, SelfUpdate, Validate};
@@ -26,6 +28,11 @@ enum WasmerCLIOptions {
#[structopt(name = "compile")] #[structopt(name = "compile")]
Compile(Compile), Compile(Compile),
/// Compile a WebAssembly binary into a native executable
#[cfg(all(feature = "object-file", feature = "compiler"))]
#[structopt(name = "create-exe")]
CreateExe(CreateExe),
/// Get various configuration information needed /// Get various configuration information needed
/// to compile programs which use Wasmer /// to compile programs which use Wasmer
#[structopt(name = "config")] #[structopt(name = "config")]
@@ -53,6 +60,8 @@ impl WasmerCLIOptions {
Self::Cache(cache) => cache.execute(), Self::Cache(cache) => cache.execute(),
Self::Validate(validate) => validate.execute(), Self::Validate(validate) => validate.execute(),
Self::Compile(compile) => compile.execute(), Self::Compile(compile) => compile.execute(),
#[cfg(all(feature = "object-file", feature = "compiler"))]
Self::CreateExe(create_exe) => create_exe.execute(),
Self::Config(config) => config.execute(), Self::Config(config) => config.execute(),
Self::Inspect(inspect) => inspect.execute(), Self::Inspect(inspect) => inspect.execute(),
#[cfg(feature = "wast")] #[cfg(feature = "wast")]
@@ -73,9 +82,8 @@ fn main() {
let args = std::env::args().collect::<Vec<_>>(); let args = std::env::args().collect::<Vec<_>>();
let command = args.get(1); let command = args.get(1);
let options = match command.unwrap_or(&"".to_string()).as_ref() { let options = match command.unwrap_or(&"".to_string()).as_ref() {
"run" | "cache" | "validate" | "compile" | "config" | "self-update" | "inspect" => { "cache" | "compile" | "config" | "create-exe" | "help" | "inspect" | "run"
WasmerCLIOptions::from_args() | "self-update" | "validate" | "wast" => WasmerCLIOptions::from_args(),
}
_ => { _ => {
WasmerCLIOptions::from_iter_safe(args.iter()).unwrap_or_else(|e| { WasmerCLIOptions::from_iter_safe(args.iter()).unwrap_or_else(|e| {
match e.kind { match e.kind {

View File

@@ -2,6 +2,8 @@
mod cache; mod cache;
mod compile; mod compile;
mod config; mod config;
#[cfg(all(feature = "object-file", feature = "compiler"))]
mod create_exe;
mod inspect; mod inspect;
mod run; mod run;
mod self_update; mod self_update;
@@ -9,6 +11,8 @@ mod validate;
#[cfg(feature = "wast")] #[cfg(feature = "wast")]
mod wast; mod wast;
#[cfg(all(feature = "object-file", feature = "compiler"))]
pub use create_exe::*;
#[cfg(feature = "wast")] #[cfg(feature = "wast")]
pub use wast::*; pub use wast::*;
pub use {cache::*, compile::*, config::*, inspect::*, run::*, self_update::*, validate::*}; pub use {cache::*, compile::*, config::*, inspect::*, run::*, self_update::*, validate::*};

View File

@@ -38,8 +38,7 @@ impl Compile {
.context(format!("failed to compile `{}`", self.path.display())) .context(format!("failed to compile `{}`", self.path.display()))
} }
fn get_recommend_extension( pub(crate) fn get_recommend_extension(
&self,
engine_type: &EngineType, engine_type: &EngineType,
target_triple: &Triple, target_triple: &Triple,
) -> &'static str { ) -> &'static str {
@@ -82,7 +81,7 @@ impl Compile {
.file_stem() .file_stem()
.map(|osstr| osstr.to_string_lossy().to_string()) .map(|osstr| osstr.to_string_lossy().to_string())
.unwrap_or_default(); .unwrap_or_default();
let recommended_extension = self.get_recommend_extension(&engine_type, target.triple()); let recommended_extension = Self::get_recommend_extension(&engine_type, target.triple());
match self.output.extension() { match self.output.extension() {
Some(ext) => { Some(ext) => {
if ext != recommended_extension { if ext != recommended_extension {

View File

@@ -0,0 +1,306 @@
//! Create a standalone native executable for a given Wasm file.
use crate::store::{CompilerOptions, EngineType};
use anyhow::{Context, Result};
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use structopt::StructOpt;
use wasmer::*;
const WASMER_MAIN_C_SOURCE: &[u8] = include_bytes!("wasmer_create_exe_main.c");
#[derive(Debug, StructOpt)]
/// The options for the `wasmer create-exe` subcommand
pub struct CreateExe {
/// Input file
#[structopt(name = "FILE", parse(from_os_str))]
path: PathBuf,
/// Output file
#[structopt(name = "OUTPUT PATH", short = "o", parse(from_os_str))]
output: PathBuf,
/// Compilation Target triple
#[structopt(long = "target")]
target_triple: Option<Triple>,
#[structopt(flatten)]
compiler: CompilerOptions,
#[structopt(short = "m", multiple = true)]
cpu_features: Vec<CpuFeature>,
/// Additional libraries to link against.
/// This is useful for fixing linker errors that may occur on some systems.
#[structopt(short = "l", multiple = true)]
libraries: Vec<String>,
}
impl CreateExe {
/// Runs logic for the `compile` subcommand
pub fn execute(&self) -> Result<()> {
let target = self
.target_triple
.as_ref()
.map(|target_triple| {
let mut features = self
.cpu_features
.clone()
.into_iter()
.fold(CpuFeature::set(), |a, b| a | b);
// Cranelift requires SSE2, so we have this "hack" for now to facilitate
// usage
features |= CpuFeature::SSE2;
Target::new(target_triple.clone(), features)
})
.unwrap_or_default();
let engine_type = EngineType::ObjectFile;
let (store, compiler_type) = self
.compiler
.get_store_for_target_and_engine(target.clone(), engine_type)?;
println!("Engine: {}", engine_type.to_string());
println!("Compiler: {}", compiler_type.to_string());
println!("Target: {}", target.triple());
let working_dir = tempfile::tempdir()?;
let starting_cd = env::current_dir()?;
let output_path = starting_cd.join(&self.output);
env::set_current_dir(&working_dir)?;
#[cfg(not(windows))]
let wasm_object_path = PathBuf::from("wasm.o");
#[cfg(windows)]
let wasm_object_path = PathBuf::from("wasm.obj");
let wasm_module_path = starting_cd.join(&self.path);
let module =
Module::from_file(&store, &wasm_module_path).context("failed to compile Wasm")?;
let _ = module.serialize_to_file(&wasm_object_path)?;
let artifact: &wasmer_engine_object_file::ObjectFileArtifact =
module.artifact().as_ref().downcast_ref().context(
"Engine type is ObjectFile but could not downcast artifact into ObjectFileArtifact",
)?;
let symbol_registry = artifact.symbol_registry();
let metadata_length = artifact.metadata_length();
let module_info = module.info();
let header_file_src = crate::c_gen::object_file_header::generate_header_file(
module_info,
symbol_registry,
metadata_length,
);
generate_header(header_file_src.as_bytes())?;
self.compile_c(wasm_object_path, output_path)?;
eprintln!(
"✔ Native executable compiled successfully to `{}`.",
self.output.display(),
);
Ok(())
}
fn compile_c(&self, wasm_object_path: PathBuf, output_path: PathBuf) -> anyhow::Result<()> {
use std::io::Write;
// write C src to disk
let c_src_path = Path::new("wasmer_main.c");
#[cfg(not(windows))]
let c_src_obj = PathBuf::from("wasmer_main.o");
#[cfg(windows)]
let c_src_obj = PathBuf::from("wasmer_main.obj");
{
let mut c_src_file = fs::OpenOptions::new()
.create_new(true)
.write(true)
.open(&c_src_path)
.context("Failed to open C source code file")?;
c_src_file.write_all(WASMER_MAIN_C_SOURCE)?;
}
run_c_compile(&c_src_path, &c_src_obj, self.target_triple.clone())
.context("Failed to compile C source code")?;
LinkCode {
object_paths: vec![c_src_obj, wasm_object_path],
output_path,
additional_libraries: self.libraries.clone(),
target: self.target_triple.clone(),
..Default::default()
}
.run()
.context("Failed to link objects together")?;
Ok(())
}
}
fn generate_header(header_file_src: &[u8]) -> anyhow::Result<()> {
let header_file_path = Path::new("my_wasm.h");
let mut header = std::fs::OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.open(&header_file_path)?;
use std::io::Write;
header.write(header_file_src)?;
Ok(())
}
fn get_wasmer_dir() -> anyhow::Result<PathBuf> {
Ok(PathBuf::from(
env::var("WASMER_DIR").context("Trying to read env var `WASMER_DIR`")?,
))
}
fn get_wasmer_include_directory() -> anyhow::Result<PathBuf> {
let mut path = get_wasmer_dir()?;
path.push("include");
Ok(path)
}
/// path to the static libwasmer
fn get_libwasmer_path() -> anyhow::Result<PathBuf> {
let mut path = get_wasmer_dir()?;
path.push("lib");
// TODO: prefer headless Wasmer if/when it's a separate library.
#[cfg(not(windows))]
path.push("libwasmer.a");
#[cfg(windows)]
path.push("wasmer_c_api.lib");
Ok(path)
}
/// Compile the C code.
fn run_c_compile(
path_to_c_src: &Path,
output_name: &Path,
target: Option<Triple>,
) -> anyhow::Result<()> {
#[cfg(not(windows))]
let c_compiler = "cc";
// We must use a C++ compiler on Windows because wasm.h uses `static_assert`
// which isn't available in `clang` on Windows.
#[cfg(windows)]
let c_compiler = "clang++";
let mut command = Command::new(c_compiler);
let command = command
.arg("-O2")
.arg("-c")
.arg(path_to_c_src)
.arg("-I")
.arg(get_wasmer_include_directory()?);
let command = if let Some(target) = target {
command.arg("-target").arg(format!("{}", target))
} else {
command
};
let output = command.arg("-o").arg(output_name).output()?;
if !output.status.success() {
bail!(
"C code compile failed with: stdout: {}\n\nstderr: {}",
std::str::from_utf8(&output.stdout)
.expect("stdout is not utf8! need to handle arbitrary bytes"),
std::str::from_utf8(&output.stderr)
.expect("stderr is not utf8! need to handle arbitrary bytes")
);
}
Ok(())
}
/// Data used to run a linking command for generated artifacts.
#[derive(Debug)]
struct LinkCode {
/// Path to the linker used to run the linking command.
linker_path: PathBuf,
/// String used as an optimization flag.
optimization_flag: String,
/// Paths of objects to link.
object_paths: Vec<PathBuf>,
/// Additional libraries to link against.
additional_libraries: Vec<String>,
/// Path to the output target.
output_path: PathBuf,
/// Path to the dir containing the static libwasmer library.
libwasmer_path: PathBuf,
/// The target to link the executable for.
target: Option<Triple>,
}
impl Default for LinkCode {
fn default() -> Self {
#[cfg(not(windows))]
let linker = "cc";
#[cfg(windows)]
let linker = "clang";
Self {
linker_path: PathBuf::from(linker),
optimization_flag: String::from("-O2"),
object_paths: vec![],
additional_libraries: vec![],
output_path: PathBuf::from("a.out"),
libwasmer_path: get_libwasmer_path().unwrap(),
target: None,
}
}
}
impl LinkCode {
fn run(&self) -> anyhow::Result<()> {
let mut command = Command::new(&self.linker_path);
let command = command
.arg(&self.optimization_flag)
.args(
self.object_paths
.iter()
.map(|path| path.canonicalize().unwrap()),
)
.arg(
&self
.libwasmer_path
.canonicalize()
.context("Failed to find libwasmer")?,
);
let command = if let Some(target) = &self.target {
command.arg("-target").arg(format!("{}", target))
} else {
command
};
// Add libraries required per platform.
// We need userenv, sockets (Ws2_32), and advapi32 to call a system call (for random numbers I think).
#[cfg(windows)]
let command = command.arg("-luserenv").arg("-lWs2_32").arg("-ladvapi32");
// On unix we need dlopen-related symbols, libmath for a few things, and pthreads.
#[cfg(not(windows))]
let command = command.arg("-ldl").arg("-lm").arg("-pthread");
let link_aganist_extra_libs = self
.additional_libraries
.iter()
.map(|lib| format!("-l{}", lib));
let command = command.args(link_aganist_extra_libs);
let output = command.arg("-o").arg(&self.output_path).output()?;
if !output.status.success() {
bail!(
"linking failed with: stdout: {}\n\nstderr: {}",
std::str::from_utf8(&output.stdout)
.expect("stdout is not utf8! need to handle arbitrary bytes"),
std::str::from_utf8(&output.stderr)
.expect("stderr is not utf8! need to handle arbitrary bytes")
);
}
Ok(())
}
}

View File

@@ -0,0 +1,172 @@
#ifdef __cplusplus
extern "C" {
#endif
#include "wasmer_wasm.h"
#include "wasm.h"
#include "my_wasm.h"
#include <stdio.h>
#include <stdlib.h>
// TODO: make this define templated so that the Rust code can toggle it on/off
#define WASI
#ifdef __cplusplus
}
#endif
void print_wasmer_error()
{
int error_len = wasmer_last_error_length();
printf("Error len: `%d`\n", error_len);
char* error_str = (char*) malloc(error_len);
wasmer_last_error_message(error_str, error_len);
printf("%s\n", error_str);
}
#ifdef WASI
int find_colon(char* string) {
int colon_location = 0;
for (int j = 0; j < strlen(string); ++j) {
if (string[j] == ':') {
colon_location = j;
break;
}
}
return colon_location;
}
void pass_mapdir_arg(wasi_config_t* wasi_config, char* mapdir) {
int colon_location = find_colon(mapdir);
if (colon_location == 0) {
// error malformed argument
fprintf(stderr, "Expected mapdir argument of the form alias:directory\n");
exit(-1);
}
int dir_len = strlen(mapdir) - colon_location;
char* alias = (char*)malloc(colon_location + 1);
char* dir = (char*)malloc(dir_len + 1);
int j = 0;
for (j = 0; j < colon_location; ++j) {
alias[j] = mapdir[j];
}
alias[j] = 0;
for (j = 0; j < dir_len; ++j) {
dir[j] = mapdir[j + colon_location + 1];
}
dir[j] = 0;
wasi_config_mapdir(wasi_config, alias, dir);
free(alias);
free(dir);
}
// We try to parse out `--dir` and `--mapdir` ahead of time and process those
// specially. All other arguments are passed to the guest program.
void handle_arguments(wasi_config_t* wasi_config, int argc, char* argv[]) {
for (int i = 1; i < argc; ++i) {
// We probably want special args like `--dir` and `--mapdir` to not be passed directly
if (strcmp(argv[i], "--dir") == 0) {
// next arg is a preopen directory
if ((i + 1) < argc ) {
i++;
wasi_config_preopen_dir(wasi_config, argv[i]);
} else {
fprintf(stderr, "--dir expects a following argument specifying which directory to preopen\n");
exit(-1);
}
}
else if (strcmp(argv[i], "--mapdir") == 0) {
// next arg is a mapdir
if ((i + 1) < argc ) {
i++;
pass_mapdir_arg(wasi_config, argv[i]);
} else {
fprintf(stderr, "--mapdir expects a following argument specifying which directory to preopen in the form alias:directory\n");
exit(-1);
}
}
else if (strncmp(argv[i], "--dir=", strlen("--dir=")) == 0 ) {
// this arg is a preopen dir
char* dir = argv[i] + strlen("--dir=");
wasi_config_preopen_dir(wasi_config, dir);
}
else if (strncmp(argv[i], "--mapdir=", strlen("--mapdir=")) == 0 ) {
// this arg is a mapdir
char* mapdir = argv[i] + strlen("--mapdir=");
pass_mapdir_arg(wasi_config, mapdir);
}
else {
// guest argument
wasi_config_arg(wasi_config, argv[i]);
}
}
}
#endif
int main(int argc, char* argv[]) {
wasm_config_t* config = wasm_config_new();
wasm_config_set_engine(config, OBJECT_FILE);
wasm_engine_t* engine = wasm_engine_new_with_config(config);
wasm_store_t* store = wasm_store_new(engine);
wasm_module_t* module = wasmer_object_file_engine_new(store, argv[0]);
if (! module) {
fprintf(stderr, "Failed to create module\n");
print_wasmer_error();
return -1;
}
// We have now finished the memory buffer book keeping and we have a valid Module.
#ifdef WASI
wasi_config_t* wasi_config = wasi_config_new(argv[0]);
handle_arguments(wasi_config, argc, argv);
wasi_env_t* wasi_env = wasi_env_new(wasi_config);
if (!wasi_env) {
fprintf(stderr, "Error building WASI env!\n");
print_wasmer_error();
return 1;
}
#endif
wasm_importtype_vec_t import_types;
wasm_module_imports(module, &import_types);
int num_imports = import_types.size;
wasm_extern_t** imports = (wasm_extern_t**) malloc(num_imports * sizeof(wasm_extern_t*));
wasm_importtype_vec_delete(&import_types);
#ifdef WASI
bool get_imports_result = wasi_get_imports(store, module, wasi_env, imports);
if (!get_imports_result) {
fprintf(stderr, "Error getting WASI imports!\n");
print_wasmer_error();
return 1;
}
#endif
wasm_instance_t* instance = wasm_instance_new(store, module, (const wasm_extern_t* const*) imports, NULL);
if (! instance) {
fprintf(stderr, "Failed to create instance\n");
print_wasmer_error();
return -1;
}
#ifdef WASI
wasi_env_set_instance(wasi_env, instance);
#endif
void* vmctx = wasm_instance_get_vmctx_ptr(instance);
wasm_val_t* inout[2] = { NULL, NULL };
// We're able to call our compiled function directly through a trampoline.
wasmer_trampoline_function_call__1(vmctx, wasmer_function__1, &inout);
wasm_instance_delete(instance);
wasm_module_delete(module);
wasm_store_delete(store);
wasm_engine_delete(engine);
return 0;
}

View File

@@ -14,8 +14,27 @@ use wasmer::*;
use wasmer_compiler::CompilerConfig; use wasmer_compiler::CompilerConfig;
#[derive(Debug, Clone, StructOpt)] #[derive(Debug, Clone, StructOpt)]
/// The compiler options /// The compiler and engine options
pub struct StoreOptions { pub struct StoreOptions {
#[structopt(flatten)]
compiler: CompilerOptions,
/// Use JIT Engine.
#[structopt(long, conflicts_with_all = &["native", "object_file"])]
jit: bool,
/// Use Native Engine.
#[structopt(long, conflicts_with_all = &["jit", "object_file"])]
native: bool,
/// Use ObjectFile Engine.
#[structopt(long, conflicts_with_all = &["jit", "native"])]
object_file: bool,
}
#[derive(Debug, Clone, StructOpt)]
/// The compiler options
pub struct CompilerOptions {
/// Use Singlepass compiler. /// Use Singlepass compiler.
#[structopt(long, conflicts_with_all = &["cranelift", "llvm", "backend"])] #[structopt(long, conflicts_with_all = &["cranelift", "llvm", "backend"])]
singlepass: bool, singlepass: bool,
@@ -36,19 +55,7 @@ pub struct StoreOptions {
#[structopt(long, parse(from_os_str))] #[structopt(long, parse(from_os_str))]
llvm_debug_dir: Option<PathBuf>, llvm_debug_dir: Option<PathBuf>,
/// Use JIT Engine. /// The deprecated backend flag - Please do not use
#[structopt(long, conflicts_with_all = &["native", "object_file"])]
jit: bool,
/// Use Native Engine.
#[structopt(long, conflicts_with_all = &["jit", "object_file"])]
native: bool,
/// Use ObjectFile Engine.
#[structopt(long, conflicts_with_all = &["jit", "native"])]
object_file: bool,
/// The deprecated backend flag - Please not use
#[structopt(long = "backend", hidden = true, conflicts_with_all = &["singlepass", "cranelift", "llvm"])] #[structopt(long = "backend", hidden = true, conflicts_with_all = &["singlepass", "cranelift", "llvm"])]
backend: Option<String>, backend: Option<String>,
@@ -56,80 +63,8 @@ pub struct StoreOptions {
features: WasmFeatures, features: WasmFeatures,
} }
/// The compiler used for the store #[cfg(feature = "compiler")]
#[derive(Debug, PartialEq, Eq)] impl CompilerOptions {
pub enum CompilerType {
/// Singlepass compiler
Singlepass,
/// Cranelift compiler
Cranelift,
/// LLVM compiler
LLVM,
/// Headless compiler
Headless,
}
impl CompilerType {
/// Return all enabled compilers
pub fn enabled() -> Vec<CompilerType> {
vec![
#[cfg(feature = "singlepass")]
Self::Singlepass,
#[cfg(feature = "cranelift")]
Self::Cranelift,
#[cfg(feature = "llvm")]
Self::LLVM,
]
}
}
impl ToString for CompilerType {
fn to_string(&self) -> String {
match self {
Self::Singlepass => "singlepass".to_string(),
Self::Cranelift => "cranelift".to_string(),
Self::LLVM => "llvm".to_string(),
Self::Headless => "headless".to_string(),
}
}
}
impl FromStr for CompilerType {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s {
"singlepass" => Ok(Self::Singlepass),
"cranelift" => Ok(Self::Cranelift),
"llvm" => Ok(Self::LLVM),
"headless" => Ok(Self::Headless),
backend => bail!("The `{}` compiler does not exist.", backend),
}
}
}
/// The engine used for the store
#[derive(Debug, PartialEq, Eq)]
pub enum EngineType {
/// JIT Engine
JIT,
/// Native Engine
Native,
/// Object File Engine
ObjectFile,
}
impl ToString for EngineType {
fn to_string(&self) -> String {
match self {
Self::JIT => "jit".to_string(),
Self::Native => "native".to_string(),
Self::ObjectFile => "objectfile".to_string(),
}
}
}
#[cfg(all(feature = "compiler", feature = "engine"))]
impl StoreOptions {
fn get_compiler(&self) -> Result<CompilerType> { fn get_compiler(&self) -> Result<CompilerType> {
if self.cranelift { if self.cranelift {
Ok(CompilerType::Cranelift) Ok(CompilerType::Cranelift)
@@ -161,7 +96,7 @@ impl StoreOptions {
} }
} }
/// Get the Target architecture /// Get the enaled Wasm features.
pub fn get_features(&self, mut features: Features) -> Result<Features> { pub fn get_features(&self, mut features: Features) -> Result<Features> {
if self.features.threads || self.features.all { if self.features.threads || self.features.all {
features.threads(true); features.threads(true);
@@ -181,6 +116,63 @@ impl StoreOptions {
Ok(features) Ok(features)
} }
/// Gets the Store for a given target and engine.
pub fn get_store_for_target_and_engine(
&self,
target: Target,
engine_type: EngineType,
) -> Result<(Store, CompilerType)> {
let (compiler_config, compiler_type) = self.get_compiler_config()?;
let engine = self.get_engine_by_type(target, compiler_config, engine_type)?;
let store = Store::new(&*engine);
Ok((store, compiler_type))
}
fn get_engine_by_type(
&self,
target: Target,
compiler_config: Box<dyn CompilerConfig>,
engine_type: EngineType,
) -> Result<Box<dyn Engine + Send + Sync>> {
let features = self.get_features(compiler_config.default_features_for_target(&target))?;
let engine: Box<dyn Engine + Send + Sync> = match engine_type {
#[cfg(feature = "jit")]
EngineType::JIT => Box::new(
wasmer_engine_jit::JIT::new(&*compiler_config)
.features(features)
.target(target)
.engine(),
),
#[cfg(feature = "native")]
EngineType::Native => {
let mut compiler_config = compiler_config;
Box::new(
wasmer_engine_native::Native::new(&mut *compiler_config)
.target(target)
.features(features)
.engine(),
)
}
#[cfg(feature = "object-file")]
EngineType::ObjectFile => {
let mut compiler_config = compiler_config;
Box::new(
wasmer_engine_object_file::ObjectFile::new(&mut *compiler_config)
.target(target)
.features(features)
.engine(),
)
}
#[cfg(not(all(feature = "jit", feature = "native", feature = "object-file")))]
engine => bail!(
"The `{}` engine is not included in this binary.",
engine.to_string()
),
};
Ok(engine)
}
/// Get the Compiler Config for the current options /// Get the Compiler Config for the current options
#[allow(unused_variables)] #[allow(unused_variables)]
pub(crate) fn get_compiler_config(&self) -> Result<(Box<dyn CompilerConfig>, CompilerType)> { pub(crate) fn get_compiler_config(&self) -> Result<(Box<dyn CompilerConfig>, CompilerType)> {
@@ -315,7 +307,82 @@ impl StoreOptions {
#[allow(unreachable_code)] #[allow(unreachable_code)]
Ok((compiler_config, compiler)) Ok((compiler_config, compiler))
} }
}
/// The compiler used for the store
#[derive(Debug, PartialEq, Eq)]
pub enum CompilerType {
/// Singlepass compiler
Singlepass,
/// Cranelift compiler
Cranelift,
/// LLVM compiler
LLVM,
/// Headless compiler
Headless,
}
impl CompilerType {
/// Return all enabled compilers
pub fn enabled() -> Vec<CompilerType> {
vec![
#[cfg(feature = "singlepass")]
Self::Singlepass,
#[cfg(feature = "cranelift")]
Self::Cranelift,
#[cfg(feature = "llvm")]
Self::LLVM,
]
}
}
impl ToString for CompilerType {
fn to_string(&self) -> String {
match self {
Self::Singlepass => "singlepass".to_string(),
Self::Cranelift => "cranelift".to_string(),
Self::LLVM => "llvm".to_string(),
Self::Headless => "headless".to_string(),
}
}
}
impl FromStr for CompilerType {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s {
"singlepass" => Ok(Self::Singlepass),
"cranelift" => Ok(Self::Cranelift),
"llvm" => Ok(Self::LLVM),
"headless" => Ok(Self::Headless),
backend => bail!("The `{}` compiler does not exist.", backend),
}
}
}
/// The engine used for the store
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum EngineType {
/// JIT Engine
JIT,
/// Native Engine
Native,
/// Object File Engine
ObjectFile,
}
impl ToString for EngineType {
fn to_string(&self) -> String {
match self {
Self::JIT => "jit".to_string(),
Self::Native => "native".to_string(),
Self::ObjectFile => "objectfile".to_string(),
}
}
}
#[cfg(all(feature = "compiler", feature = "engine"))]
impl StoreOptions {
/// Gets the store for the host target, with the engine name and compiler name selected /// Gets the store for the host target, with the engine name and compiler name selected
pub fn get_store(&self) -> Result<(Store, EngineType, CompilerType)> { pub fn get_store(&self) -> Result<(Store, EngineType, CompilerType)> {
let target = Target::default(); let target = Target::default();
@@ -327,7 +394,7 @@ impl StoreOptions {
&self, &self,
target: Target, target: Target,
) -> Result<(Store, EngineType, CompilerType)> { ) -> Result<(Store, EngineType, CompilerType)> {
let (compiler_config, compiler_type) = self.get_compiler_config()?; let (compiler_config, compiler_type) = self.compiler.get_compiler_config()?;
let (engine, engine_type) = self.get_engine_with_compiler(target, compiler_config)?; let (engine, engine_type) = self.get_engine_with_compiler(target, compiler_config)?;
let store = Store::new(&*engine); let store = Store::new(&*engine);
Ok((store, engine_type, compiler_type)) Ok((store, engine_type, compiler_type))
@@ -339,41 +406,10 @@ impl StoreOptions {
compiler_config: Box<dyn CompilerConfig>, compiler_config: Box<dyn CompilerConfig>,
) -> Result<(Box<dyn Engine + Send + Sync>, EngineType)> { ) -> Result<(Box<dyn Engine + Send + Sync>, EngineType)> {
let engine_type = self.get_engine()?; let engine_type = self.get_engine()?;
let features = self.get_features(compiler_config.default_features_for_target(&target))?; let engine = self
let engine: Box<dyn Engine + Send + Sync> = match engine_type { .compiler
#[cfg(feature = "jit")] .get_engine_by_type(target, compiler_config, engine_type)?;
EngineType::JIT => Box::new(
wasmer_engine_jit::JIT::new(&*compiler_config)
.features(features)
.target(target)
.engine(),
),
#[cfg(feature = "native")]
EngineType::Native => {
let mut compiler_config = compiler_config;
Box::new(
wasmer_engine_native::Native::new(&mut *compiler_config)
.target(target)
.features(features)
.engine(),
)
}
#[cfg(feature = "object-file")]
EngineType::ObjectFile => {
let mut compiler_config = compiler_config;
Box::new(
wasmer_engine_object_file::ObjectFile::new(&mut *compiler_config)
.target(target)
.features(features)
.engine(),
)
}
#[cfg(not(all(feature = "jit", feature = "native", feature = "object-file")))]
engine => bail!(
"The `{}` engine is not included in this binary.",
engine.to_string()
),
};
Ok((engine, engine_type)) Ok((engine, engine_type))
} }
} }

View File

@@ -15,7 +15,7 @@ edition = "2018"
wasmer-compiler = { path = "../compiler", version = "1.0.0-alpha4", features = ["translator"], default-features = false } wasmer-compiler = { path = "../compiler", version = "1.0.0-alpha4", features = ["translator"], default-features = false }
wasmer-vm = { path = "../vm", version = "1.0.0-alpha4" } wasmer-vm = { path = "../vm", version = "1.0.0-alpha4" }
wasmer-types = { path = "../wasmer-types", version = "1.0.0-alpha4", default-features = false, features = ["std"] } wasmer-types = { path = "../wasmer-types", version = "1.0.0-alpha4", default-features = false, features = ["std"] }
cranelift-codegen = { version = "0.65", default-features = false } cranelift-codegen = { version = "0.65", default-features = false, features = ["x86", "arm64"] }
cranelift-frontend = { version = "0.65", default-features = false } cranelift-frontend = { version = "0.65", default-features = false }
tracing = "0.1" tracing = "0.1"
hashbrown = { version = "0.8", optional = true } hashbrown = { version = "0.8", optional = true }

View File

@@ -4,7 +4,7 @@
//! Offsets and sizes of various structs in wasmer-vm's vmcontext //! Offsets and sizes of various structs in wasmer-vm's vmcontext
//! module. //! module.
#![deny(intra_doc_link_resolution_failure)] #![deny(broken_intra_doc_links)]
use crate::module::ModuleInfo; use crate::module::ModuleInfo;
use crate::VMBuiltinFunctionIndex; use crate::VMBuiltinFunctionIndex;

View File

@@ -8,10 +8,14 @@ use crate::types::Type;
/// produce. /// produce.
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub enum Value<T> { pub enum Value<T> {
/// A 32-bit integer /// A 32-bit integer.
///
/// In Wasm integers are sign-agnostic, i.e. this can either be signed or unsigned.
I32(i32), I32(i32),
/// A 64-bit integer /// A 64-bit integer.
///
/// In Wasm integers are sign-agnostic, i.e. this can either be signed or unsigned.
I64(i64), I64(i64),
/// A 32-bit float. /// A 32-bit float.
@@ -175,12 +179,26 @@ impl<T> From<i32> for Value<T> {
} }
} }
impl<T> From<u32> for Value<T> {
fn from(val: u32) -> Self {
// In Wasm integers are sign-agnostic, so i32 is basically a 4 byte storage we can use for signed or unsigned 32-bit integers.
Self::I32(val as i32)
}
}
impl<T> From<i64> for Value<T> { impl<T> From<i64> for Value<T> {
fn from(val: i64) -> Self { fn from(val: i64) -> Self {
Self::I64(val) Self::I64(val)
} }
} }
impl<T> From<u64> for Value<T> {
fn from(val: u64) -> Self {
// In Wasm integers are sign-agnostic, so i64 is basically an 8 byte storage we can use for signed or unsigned 64-bit integers.
Self::I64(val as i64)
}
}
impl<T> From<f32> for Value<T> { impl<T> From<f32> for Value<T> {
fn from(val: f32) -> Self { fn from(val: f32) -> Self {
Self::F32(val) Self::F32(val)
@@ -204,3 +222,46 @@ impl<T> From<ExternRef> for Value<T> {
// Self::FuncRef(val) // Self::FuncRef(val)
// } // }
// } // }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_value_i32_from_u32() {
let bytes = [0x00, 0x00, 0x00, 0x00];
let v = Value::<()>::from(u32::from_be_bytes(bytes.clone()));
assert_eq!(v, Value::I32(i32::from_be_bytes(bytes.clone())));
let bytes = [0x00, 0x00, 0x00, 0x01];
let v = Value::<()>::from(u32::from_be_bytes(bytes.clone()));
assert_eq!(v, Value::I32(i32::from_be_bytes(bytes.clone())));
let bytes = [0xAA, 0xBB, 0xCC, 0xDD];
let v = Value::<()>::from(u32::from_be_bytes(bytes.clone()));
assert_eq!(v, Value::I32(i32::from_be_bytes(bytes.clone())));
let bytes = [0xFF, 0xFF, 0xFF, 0xFF];
let v = Value::<()>::from(u32::from_be_bytes(bytes.clone()));
assert_eq!(v, Value::I32(i32::from_be_bytes(bytes.clone())));
}
#[test]
fn test_value_i64_from_u64() {
let bytes = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
let v = Value::<()>::from(u64::from_be_bytes(bytes.clone()));
assert_eq!(v, Value::I64(i64::from_be_bytes(bytes.clone())));
let bytes = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01];
let v = Value::<()>::from(u64::from_be_bytes(bytes.clone()));
assert_eq!(v, Value::I64(i64::from_be_bytes(bytes.clone())));
let bytes = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11];
let v = Value::<()>::from(u64::from_be_bytes(bytes.clone()));
assert_eq!(v, Value::I64(i64::from_be_bytes(bytes.clone())));
let bytes = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
let v = Value::<()>::from(u64::from_be_bytes(bytes.clone()));
assert_eq!(v, Value::I64(i64::from_be_bytes(bytes.clone())));
}
}

View File

@@ -38,7 +38,14 @@ fn test_trap_return() -> Result<()> {
} }
#[test] #[test]
#[cfg_attr(feature = "test-singlepass", ignore)] #[cfg_attr(
any(
feature = "test-singlepass",
feature = "test-native",
target_arch = "aarch64",
),
ignore
)]
fn test_trap_trace() -> Result<()> { fn test_trap_trace() -> Result<()> {
let store = get_store(); let store = get_store();
let wat = r#" let wat = r#"
@@ -118,7 +125,14 @@ fn test_trap_trace_cb() -> Result<()> {
} }
#[test] #[test]
#[cfg_attr(feature = "test-singlepass", ignore)] #[cfg_attr(
any(
feature = "test-singlepass",
feature = "test-native",
target_arch = "aarch64",
),
ignore
)]
fn test_trap_stack_overflow() -> Result<()> { fn test_trap_stack_overflow() -> Result<()> {
let store = get_store(); let store = get_store();
let wat = r#" let wat = r#"
@@ -149,7 +163,15 @@ fn test_trap_stack_overflow() -> Result<()> {
} }
#[test] #[test]
#[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)] #[cfg_attr(
any(
feature = "test-singlepass",
feature = "test-llvm",
feature = "test-native",
target_arch = "aarch64",
),
ignore
)]
fn trap_display_pretty() -> Result<()> { fn trap_display_pretty() -> Result<()> {
let store = get_store(); let store = get_store();
let wat = r#" let wat = r#"
@@ -182,7 +204,15 @@ RuntimeError: unreachable
} }
#[test] #[test]
#[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)] #[cfg_attr(
any(
feature = "test-singlepass",
feature = "test-llvm",
feature = "test-native",
target_arch = "aarch64",
),
ignore
)]
fn trap_display_multi_module() -> Result<()> { fn trap_display_multi_module() -> Result<()> {
let store = get_store(); let store = get_store();
let wat = r#" let wat = r#"
@@ -387,7 +417,14 @@ fn mismatched_arguments() -> Result<()> {
} }
#[test] #[test]
#[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)] #[cfg_attr(
any(
feature = "test-singlepass",
feature = "test-llvm",
feature = "test-native"
),
ignore
)]
fn call_signature_mismatch() -> Result<()> { fn call_signature_mismatch() -> Result<()> {
let store = get_store(); let store = get_store();
let binary = r#" let binary = r#"
@@ -418,7 +455,15 @@ RuntimeError: indirect call type mismatch
} }
#[test] #[test]
#[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)] #[cfg_attr(
any(
feature = "test-singlepass",
feature = "test-llvm",
feature = "test-native",
target_arch = "aarch64",
),
ignore
)]
fn start_trap_pretty() -> Result<()> { fn start_trap_pretty() -> Result<()> {
let store = get_store(); let store = get_store();
let wat = r#" let wat = r#"
@@ -450,6 +495,7 @@ RuntimeError: unreachable
} }
#[test] #[test]
#[cfg_attr(feature = "test-native", ignore)]
fn present_after_module_drop() -> Result<()> { fn present_after_module_drop() -> Result<()> {
let store = get_store(); let store = get_store();
let module = Module::new(&store, r#"(func (export "foo") unreachable)"#)?; let module = Module::new(&store, r#"(func (export "foo") unreachable)"#)?;

View File

@@ -2,7 +2,10 @@ use std::sync::Arc;
use wasmer::{FunctionMiddlewareGenerator, Store}; use wasmer::{FunctionMiddlewareGenerator, Store};
use wasmer_compiler::CompilerConfig; use wasmer_compiler::CompilerConfig;
use wasmer_engine::Engine; use wasmer_engine::Engine;
#[cfg(feature = "test-jit")]
use wasmer_engine_jit::JIT; use wasmer_engine_jit::JIT;
#[cfg(feature = "test-native")]
use wasmer_engine_native::Native;
pub fn get_compiler(canonicalize_nans: bool) -> impl CompilerConfig { pub fn get_compiler(canonicalize_nans: bool) -> impl CompilerConfig {
cfg_if::cfg_if! { cfg_if::cfg_if! {
@@ -32,10 +35,16 @@ pub fn get_compiler(canonicalize_nans: bool) -> impl CompilerConfig {
} }
} }
#[cfg(feature = "test-jit")]
pub fn get_engine() -> impl Engine { pub fn get_engine() -> impl Engine {
let compiler_config = get_compiler(false); let compiler_config = get_compiler(false);
JIT::new(&compiler_config).engine() JIT::new(&compiler_config).engine()
} }
#[cfg(feature = "test-native")]
pub fn get_engine() -> impl Engine {
let mut compiler_config = get_compiler(false);
Native::new(&mut compiler_config).engine()
}
pub fn get_store() -> Store { pub fn get_store() -> Store {
Store::new(&get_engine()) Store::new(&get_engine())
@@ -48,10 +57,20 @@ pub fn get_store_with_middlewares<I: Iterator<Item = Arc<dyn FunctionMiddlewareG
for x in middlewares { for x in middlewares {
compiler_config.push_middleware(x); compiler_config.push_middleware(x);
} }
#[cfg(feature = "test-jit")]
let engine = JIT::new(&compiler_config).engine(); let engine = JIT::new(&compiler_config).engine();
#[cfg(feature = "test-native")]
let engine = Native::new(&mut compiler_config).engine();
Store::new(&engine) Store::new(&engine)
} }
#[cfg(feature = "test-jit")]
pub fn get_headless_store() -> Store { pub fn get_headless_store() -> Store {
Store::new(&JIT::headless().engine()) Store::new(&JIT::headless().engine())
// Store::new(&Native::headless().engine())
}
#[cfg(feature = "test-native")]
pub fn get_headless_store() -> Store {
Store::new(&Native::headless().engine())
} }

View File

@@ -3,10 +3,10 @@
use crate::utils::get_compiler; use crate::utils::get_compiler;
use std::path::Path; use std::path::Path;
use wasmer::{Features, Store}; use wasmer::{Features, Store};
#[cfg(feature = "jit")] #[cfg(feature = "test-jit")]
use wasmer_engine_jit::JIT; use wasmer_engine_jit::JIT;
// #[cfg(feature = "native")] #[cfg(feature = "test-native")]
// use wasmer_engine_native::NativeEngine; use wasmer_engine_native::Native;
use wasmer_wast::Wast; use wasmer_wast::Wast;
// The generated tests (from build.rs) look like: // The generated tests (from build.rs) look like:
@@ -28,6 +28,22 @@ fn _native_prefixer(bytes: &[u8]) -> String {
format!("{}", hash.to_hex()) format!("{}", hash.to_hex())
} }
#[cfg(feature = "test-jit")]
fn get_store(features: Features, try_nan_canonicalization: bool) -> Store {
let compiler_config = get_compiler(try_nan_canonicalization);
Store::new(&JIT::new(&compiler_config).features(features).engine())
}
#[cfg(feature = "test-native")]
fn get_store(features: Features, try_nan_canonicalization: bool) -> Store {
let mut compiler_config = get_compiler(try_nan_canonicalization);
Store::new(
&Native::new(&mut compiler_config)
.features(features)
.engine(),
)
}
pub fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> { pub fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> {
println!( println!(
"Running wast `{}` with the {} compiler", "Running wast `{}` with the {} compiler",
@@ -43,14 +59,28 @@ pub fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> {
if is_simd { if is_simd {
features.simd(true); features.simd(true);
} }
#[cfg(feature = "test-singlepass")] if cfg!(feature = "test-singlepass") {
features.multi_value(false); features.multi_value(false);
let compiler_config = get_compiler(try_nan_canonicalization); }
let store = Store::new(&JIT::new(&compiler_config).features(features).engine()); let store = get_store(features, try_nan_canonicalization);
// let mut native = NativeEngine::new(compiler_config, tunables);
// native.set_deterministic_prefixer(native_prefixer);
// let store = Store::new(&native);
let mut wast = Wast::new_with_spectest(store); let mut wast = Wast::new_with_spectest(store);
// `bulk-memory-operations/bulk.wast` checks for a message that
// specifies which element is uninitialized, but our traps don't
// shepherd that information out.
wast.allow_trap_message("uninitialized element 2", "uninitialized element");
if compiler == "cranelift" && cfg!(feature = "test-native") {
wast.allow_trap_message("call stack exhausted", "out of bounds memory access");
wast.allow_trap_message("indirect call type mismatch", "call stack exhausted");
wast.allow_trap_message("integer divide by zero", "call stack exhausted");
wast.allow_trap_message("integer overflow", "call stack exhausted");
wast.allow_trap_message("invalid conversion to integer", "call stack exhausted");
wast.allow_trap_message("undefined element", "call stack exhausted");
wast.allow_trap_message("uninitialized element", "call stack exhausted");
wast.allow_trap_message("unreachable", "call stack exhausted");
}
if cfg!(feature = "coverage") {
wast.disable_assert_and_exhaustion();
}
if is_simd { if is_simd {
// We allow this, so tests can be run properly for `simd_const` test. // We allow this, so tests can be run properly for `simd_const` test.
wast.allow_instantiation_failures(&[ wast.allow_instantiation_failures(&[

View File

@@ -22,6 +22,29 @@ singlepass on windows # Singlepass is not yet supported on Windows
cranelift::spec::skip_stack_guard_page on darwin cranelift::spec::skip_stack_guard_page on darwin
llvm::spec::skip_stack_guard_page on darwin llvm::spec::skip_stack_guard_page on darwin
# TODO(https://github.com/wasmerio/wasmer/issues/1727): Traps in native engine
cranelift::spec::linking on native
# TODO(https://github.com/wasmerio/wasmer/pull/1710): Library is dropped too soon.
llvm::spec::linking on native
# https://github.com/wasmerio/wasmer/issues/1722
llvm::spec::skip_stack_guard_page on native
# Cranelift on arm failures
cranelift::spec::align on aarch64
cranelift::spec::memory_trap on aarch64
cranelift::spec::simd::simd_bit_shift on aarch64
cranelift::spec::simd::simd_boolean on aarch64
cranelift::spec::simd::simd_const on aarch64
cranelift::spec::simd::simd_i16x8_arith on aarch64
cranelift::spec::simd::simd_i16x8_sat_arith on aarch64
cranelift::spec::simd::simd_i32x4_arith on aarch64
cranelift::spec::simd::simd_i64x2_arith on aarch64
cranelift::spec::simd::simd_i8x16_arith on aarch64
cranelift::spec::simd::simd_i8x16_sat_arith on aarch64
cranelift::spec::simd::simd_lane on aarch64
cranelift::spec::skip_stack_guard_page on aarch64
# Frontends # Frontends
## WASI ## WASI

View File

@@ -0,0 +1,32 @@
use std::env;
use std::path::PathBuf;
pub const ASSET_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets");
pub const WASMER_PATH: &str = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../../target/release/wasmer"
);
#[cfg(not(windows))]
pub const LIBWASMER_PATH: &str = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../../target/release/libwasmer_c_api.a"
);
#[cfg(windows)]
pub const LIBWASMER_PATH: &str = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../../target/release/wasmer_c_api.lib"
);
/// Get the path to the `libwasmer.a` static library.
pub fn get_libwasmer_path() -> PathBuf {
PathBuf::from(
env::var("WASMER_TEST_LIBWASMER_PATH").unwrap_or_else(|_| LIBWASMER_PATH.to_string()),
)
}
/// Get the path to the `wasmer` executable to be used in this test.
pub fn get_wasmer_path() -> PathBuf {
PathBuf::from(env::var("WASMER_TEST_WASMER_PATH").unwrap_or_else(|_| WASMER_PATH.to_string()))
}

View File

@@ -1,306 +0,0 @@
//! CLI tests for the compile subcommand.
use anyhow::{bail, Context};
use std::env;
use std::fs;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;
const CLI_INTEGRATION_TESTS_ASSETS: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets");
const OBJECT_FILE_ENGINE_TEST_C_SOURCE: &[u8] =
include_bytes!("object_file_engine_test_c_source.c");
// TODO:
const OBJECT_FILE_ENGINE_TEST_WASM_PATH: &str =
concat!(env!("CARGO_MANIFEST_DIR"), "/assets/qjs.wasm");
const WASMER_PATH: &str = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../../target/release/wasmer"
);
#[cfg(not(windows))]
const LIBWASMER_PATH: &str = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../../target/release/libwasmer_c_api.a"
);
#[cfg(windows)]
const LIBWASMER_PATH: &str = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../../target/release/wasmer_c_api.lib"
);
/// Get the path to the `wasmer` executable to be used in this test.
fn get_wasmer_path() -> PathBuf {
PathBuf::from(env::var("WASMER_TEST_WASMER_PATH").unwrap_or_else(|_| WASMER_PATH.to_string()))
}
/// Get the path to the `libwasmer.a` static library.
fn get_libwasmer_path() -> PathBuf {
PathBuf::from(
env::var("WASMER_TEST_LIBWASMER_PATH").unwrap_or_else(|_| LIBWASMER_PATH.to_string()),
)
}
#[allow(dead_code)]
#[derive(Debug, Copy, Clone)]
pub enum Engine {
Jit,
Native,
ObjectFile,
}
impl Engine {
// TODO: make this `const fn` when Wasmer moves to Rust 1.46.0+
pub fn to_flag(self) -> &'static str {
match self {
Engine::Jit => "--jit",
Engine::Native => "--native",
Engine::ObjectFile => "--object-file",
}
}
}
#[allow(dead_code)]
#[derive(Debug, Copy, Clone)]
pub enum Compiler {
Cranelift,
LLVM,
Singlepass,
}
impl Compiler {
// TODO: make this `const fn` when Wasmer moves to Rust 1.46.0+
pub fn to_flag(self) -> &'static str {
match self {
Compiler::Cranelift => "--cranelift",
Compiler::LLVM => "--llvm",
Compiler::Singlepass => "--singlepass",
}
}
}
/// Data used to run the `wasmer compile` command.
#[derive(Debug)]
struct WasmerCompile {
/// Path to wasmer executable used to run the command.
wasmer_path: PathBuf,
/// Path to the Wasm file to compile.
wasm_path: PathBuf,
/// Path to the object file produced by compiling the Wasm.
wasm_object_path: PathBuf,
/// Path to output the generated header to.
header_output_path: PathBuf,
/// Compiler with which to compile the Wasm.
compiler: Compiler,
/// Engine with which to use to generate the artifacts.
engine: Engine,
}
impl Default for WasmerCompile {
fn default() -> Self {
#[cfg(not(windows))]
let wasm_obj_path = "wasm.o";
#[cfg(windows)]
let wasm_obj_path = "wasm.obj";
Self {
wasmer_path: get_wasmer_path(),
wasm_path: PathBuf::from(OBJECT_FILE_ENGINE_TEST_WASM_PATH),
wasm_object_path: PathBuf::from(wasm_obj_path),
header_output_path: PathBuf::from("my_wasm.h"),
compiler: Compiler::Cranelift,
engine: Engine::ObjectFile,
}
}
}
impl WasmerCompile {
fn run(&self) -> anyhow::Result<()> {
let output = Command::new(&self.wasmer_path)
.arg("compile")
.arg(&self.wasm_path.canonicalize()?)
.arg(&self.compiler.to_flag())
.arg(&self.engine.to_flag())
.arg("-o")
.arg(&self.wasm_object_path)
.arg("--header")
.arg(&self.header_output_path)
.output()?;
if !output.status.success() {
bail!(
"wasmer compile failed with: stdout: {}\n\nstderr: {}",
std::str::from_utf8(&output.stdout)
.expect("stdout is not utf8! need to handle arbitrary bytes"),
std::str::from_utf8(&output.stderr)
.expect("stderr is not utf8! need to handle arbitrary bytes")
);
}
Ok(())
}
}
/// Compile the C code.
fn run_c_compile(path_to_c_src: &Path, output_name: &Path) -> anyhow::Result<()> {
#[cfg(not(windows))]
let c_compiler = "cc";
#[cfg(windows)]
let c_compiler = "clang++";
let output = Command::new(c_compiler)
.arg("-O2")
.arg("-c")
.arg(path_to_c_src)
.arg("-I")
.arg(CLI_INTEGRATION_TESTS_ASSETS)
.arg("-o")
.arg(output_name)
.output()?;
if !output.status.success() {
bail!(
"C code compile failed with: stdout: {}\n\nstderr: {}",
std::str::from_utf8(&output.stdout)
.expect("stdout is not utf8! need to handle arbitrary bytes"),
std::str::from_utf8(&output.stderr)
.expect("stderr is not utf8! need to handle arbitrary bytes")
);
}
Ok(())
}
/// Data used to run a linking command for generated artifacts.
#[derive(Debug)]
struct LinkCode {
/// Path to the linker used to run the linking command.
linker_path: PathBuf,
/// String used as an optimization flag.
optimization_flag: String,
/// Paths of objects to link.
object_paths: Vec<PathBuf>,
/// Path to the output target.
output_path: PathBuf,
/// Path to the static libwasmer library.
libwasmer_path: PathBuf,
}
impl Default for LinkCode {
fn default() -> Self {
#[cfg(not(windows))]
let linker = "cc";
#[cfg(windows)]
let linker = "clang";
Self {
linker_path: PathBuf::from(linker),
optimization_flag: String::from("-O2"),
object_paths: vec![],
output_path: PathBuf::from("a.out"),
libwasmer_path: get_libwasmer_path(),
}
}
}
impl LinkCode {
fn run(&self) -> anyhow::Result<()> {
let mut command = Command::new(&self.linker_path);
let command = command
.arg(&self.optimization_flag)
.args(
self.object_paths
.iter()
.map(|path| path.canonicalize().unwrap()),
)
.arg(&self.libwasmer_path.canonicalize()?);
#[cfg(windows)]
let command = command.arg("-luserenv").arg("-lWs2_32").arg("-ladvapi32");
#[cfg(not(windows))]
let command = command.arg("-ldl").arg("-lm").arg("-pthread");
let output = command.arg("-o").arg(&self.output_path).output()?;
if !output.status.success() {
bail!(
"linking failed with: stdout: {}\n\nstderr: {}",
std::str::from_utf8(&output.stdout)
.expect("stdout is not utf8! need to handle arbitrary bytes"),
std::str::from_utf8(&output.stderr)
.expect("stderr is not utf8! need to handle arbitrary bytes")
);
}
Ok(())
}
}
fn run_code(executable_path: &Path) -> anyhow::Result<String> {
let output = Command::new(executable_path.canonicalize()?).output()?;
if !output.status.success() {
bail!(
"running executable failed: stdout: {}\n\nstderr: {}",
std::str::from_utf8(&output.stdout)
.expect("stdout is not utf8! need to handle arbitrary bytes"),
std::str::from_utf8(&output.stderr)
.expect("stderr is not utf8! need to handle arbitrary bytes")
);
}
let output =
std::str::from_utf8(&output.stdout).expect("output from running executable is not utf-8");
Ok(output.to_owned())
}
#[test]
fn object_file_engine_works() -> anyhow::Result<()> {
let operating_dir = tempfile::tempdir()?;
std::env::set_current_dir(&operating_dir)?;
let wasm_path = PathBuf::from(OBJECT_FILE_ENGINE_TEST_WASM_PATH);
#[cfg(not(windows))]
let wasm_object_path = PathBuf::from("wasm.o");
#[cfg(windows)]
let wasm_object_path = PathBuf::from("wasm.obj");
let header_output_path = PathBuf::from("my_wasm.h");
WasmerCompile {
wasm_path: wasm_path.clone(),
wasm_object_path: wasm_object_path.clone(),
header_output_path,
compiler: Compiler::Cranelift,
engine: Engine::ObjectFile,
..Default::default()
}
.run()
.context("Failed to compile wasm with Wasmer")?;
let c_src_file_name = Path::new("c_src.c");
#[cfg(not(windows))]
let c_object_path = PathBuf::from("c_src.o");
#[cfg(windows)]
let c_object_path = PathBuf::from("c_src.obj");
let executable_path = PathBuf::from("a.out");
// TODO: adjust C source code based on locations of things
{
let mut c_src_file = fs::OpenOptions::new()
.create_new(true)
.write(true)
.open(&c_src_file_name)
.context("Failed to open C source code file")?;
c_src_file.write_all(OBJECT_FILE_ENGINE_TEST_C_SOURCE)?;
}
run_c_compile(&c_src_file_name, &c_object_path).context("Failed to compile C source code")?;
LinkCode {
object_paths: vec![c_object_path, wasm_object_path],
output_path: executable_path.clone(),
..Default::default()
}
.run()
.context("Failed to link objects together")?;
let result = run_code(&executable_path).context("Failed to run generated executable")?;
let result_lines = result.lines().collect::<Vec<&str>>();
assert_eq!(result_lines, vec!["Initializing...", "\"Hello, World\""],);
Ok(())
}

View File

@@ -1,6 +1,10 @@
#![forbid(unsafe_code)] #![forbid(unsafe_code)]
#![cfg(test)]
//! CLI integration tests //! CLI integration tests
mod compile; pub mod assets;
pub mod link_code;
pub mod util;
pub use assets::*;
pub use util::*;

View File

@@ -0,0 +1,69 @@
use crate::assets::*;
use anyhow::bail;
use std::path::PathBuf;
use std::process::Command;
/// Data used to run a linking command for generated artifacts.
#[derive(Debug)]
pub struct LinkCode {
/// The directory to operate in.
pub current_dir: PathBuf,
/// Path to the linker used to run the linking command.
pub linker_path: PathBuf,
/// String used as an optimization flag.
pub optimization_flag: String,
/// Paths of objects to link.
pub object_paths: Vec<PathBuf>,
/// Path to the output target.
pub output_path: PathBuf,
/// Path to the static libwasmer library.
pub libwasmer_path: PathBuf,
}
impl Default for LinkCode {
fn default() -> Self {
#[cfg(not(windows))]
let linker = "cc";
#[cfg(windows)]
let linker = "clang";
Self {
current_dir: std::env::current_dir().unwrap(),
linker_path: PathBuf::from(linker),
optimization_flag: String::from("-O2"),
object_paths: vec![],
output_path: PathBuf::from("a.out"),
libwasmer_path: get_libwasmer_path(),
}
}
}
impl LinkCode {
pub fn run(&self) -> anyhow::Result<()> {
let mut command = Command::new(&self.linker_path);
let command = command
.current_dir(&self.current_dir)
.arg(&self.optimization_flag)
.args(
self.object_paths
.iter()
.map(|path| path.canonicalize().unwrap()),
)
.arg(&self.libwasmer_path.canonicalize()?);
#[cfg(windows)]
let command = command.arg("-luserenv").arg("-lWs2_32").arg("-ladvapi32");
#[cfg(not(windows))]
let command = command.arg("-ldl").arg("-lm").arg("-pthread");
let output = command.arg("-o").arg(&self.output_path).output()?;
if !output.status.success() {
bail!(
"linking failed with: stdout: {}\n\nstderr: {}",
std::str::from_utf8(&output.stdout)
.expect("stdout is not utf8! need to handle arbitrary bytes"),
std::str::from_utf8(&output.stderr)
.expect("stderr is not utf8! need to handle arbitrary bytes")
);
}
Ok(())
}
}

View File

@@ -0,0 +1,62 @@
use anyhow::bail;
use std::path::Path;
use std::process::Command;
#[derive(Debug, Copy, Clone)]
pub enum Compiler {
Cranelift,
LLVM,
Singlepass,
}
impl Compiler {
pub const fn to_flag(self) -> &'static str {
match self {
Compiler::Cranelift => "--cranelift",
Compiler::LLVM => "--llvm",
Compiler::Singlepass => "--singlepass",
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum Engine {
Jit,
Native,
ObjectFile,
}
impl Engine {
pub const fn to_flag(self) -> &'static str {
match self {
Engine::Jit => "--jit",
Engine::Native => "--native",
Engine::ObjectFile => "--object-file",
}
}
}
pub fn run_code(
operating_dir: &Path,
executable_path: &Path,
args: &[String],
) -> anyhow::Result<String> {
let output = Command::new(executable_path.canonicalize()?)
.current_dir(operating_dir)
.args(args)
.output()?;
if !output.status.success() {
bail!(
"running executable failed: stdout: {}\n\nstderr: {}",
std::str::from_utf8(&output.stdout)
.expect("stdout is not utf8! need to handle arbitrary bytes"),
std::str::from_utf8(&output.stderr)
.expect("stderr is not utf8! need to handle arbitrary bytes")
);
}
let output =
std::str::from_utf8(&output.stdout).expect("output from running executable is not utf-8");
Ok(output.to_owned())
}

View File

@@ -0,0 +1,173 @@
//! CLI tests for the compile subcommand.
use anyhow::{bail, Context};
use std::fs;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;
use wasmer_integration_tests_cli::link_code::*;
use wasmer_integration_tests_cli::*;
const OBJECT_FILE_ENGINE_TEST_C_SOURCE: &[u8] =
include_bytes!("object_file_engine_test_c_source.c");
fn object_file_engine_test_wasm_path() -> String {
format!("{}/{}", ASSET_PATH, "qjs.wasm")
}
/// Data used to run the `wasmer compile` command.
#[derive(Debug)]
struct WasmerCompile {
/// The directory to operate in.
current_dir: PathBuf,
/// Path to wasmer executable used to run the command.
wasmer_path: PathBuf,
/// Path to the Wasm file to compile.
wasm_path: PathBuf,
/// Path to the object file produced by compiling the Wasm.
wasm_object_path: PathBuf,
/// Path to output the generated header to.
header_output_path: PathBuf,
/// Compiler with which to compile the Wasm.
compiler: Compiler,
/// Engine with which to use to generate the artifacts.
engine: Engine,
}
impl Default for WasmerCompile {
fn default() -> Self {
#[cfg(not(windows))]
let wasm_obj_path = "wasm.o";
#[cfg(windows)]
let wasm_obj_path = "wasm.obj";
Self {
current_dir: std::env::current_dir().unwrap(),
wasmer_path: get_wasmer_path(),
wasm_path: PathBuf::from(object_file_engine_test_wasm_path()),
wasm_object_path: PathBuf::from(wasm_obj_path),
header_output_path: PathBuf::from("my_wasm.h"),
compiler: Compiler::Cranelift,
engine: Engine::ObjectFile,
}
}
}
impl WasmerCompile {
fn run(&self) -> anyhow::Result<()> {
let output = Command::new(&self.wasmer_path)
.current_dir(&self.current_dir)
.arg("compile")
.arg(&self.wasm_path.canonicalize()?)
.arg(&self.compiler.to_flag())
.arg(&self.engine.to_flag())
.arg("-o")
.arg(&self.wasm_object_path)
.arg("--header")
.arg(&self.header_output_path)
.output()?;
if !output.status.success() {
bail!(
"wasmer compile failed with: stdout: {}\n\nstderr: {}",
std::str::from_utf8(&output.stdout)
.expect("stdout is not utf8! need to handle arbitrary bytes"),
std::str::from_utf8(&output.stderr)
.expect("stderr is not utf8! need to handle arbitrary bytes")
);
}
Ok(())
}
}
/// Compile the C code.
fn run_c_compile(
current_dir: &Path,
path_to_c_src: &Path,
output_name: &Path,
) -> anyhow::Result<()> {
#[cfg(not(windows))]
let c_compiler = "cc";
#[cfg(windows)]
let c_compiler = "clang++";
let output = Command::new(c_compiler)
.current_dir(current_dir)
.arg("-O2")
.arg("-c")
.arg(path_to_c_src)
.arg("-I")
.arg(ASSET_PATH)
.arg("-o")
.arg(output_name)
.output()?;
if !output.status.success() {
bail!(
"C code compile failed with: stdout: {}\n\nstderr: {}",
std::str::from_utf8(&output.stdout)
.expect("stdout is not utf8! need to handle arbitrary bytes"),
std::str::from_utf8(&output.stderr)
.expect("stderr is not utf8! need to handle arbitrary bytes")
);
}
Ok(())
}
#[test]
fn object_file_engine_works() -> anyhow::Result<()> {
let temp_dir = tempfile::tempdir()?;
let operating_dir: PathBuf = temp_dir.path().to_owned();
let wasm_path = operating_dir.join(object_file_engine_test_wasm_path());
#[cfg(not(windows))]
let wasm_object_path = operating_dir.join("wasm.o");
#[cfg(windows)]
let wasm_object_path = operating_dir.join("wasm.obj");
let header_output_path = operating_dir.join("my_wasm.h");
WasmerCompile {
current_dir: operating_dir.clone(),
wasm_path: wasm_path.clone(),
wasm_object_path: wasm_object_path.clone(),
header_output_path,
compiler: Compiler::Cranelift,
engine: Engine::ObjectFile,
..Default::default()
}
.run()
.context("Failed to compile wasm with Wasmer")?;
let c_src_file_name = operating_dir.join("c_src.c");
#[cfg(not(windows))]
let c_object_path = operating_dir.join("c_src.o");
#[cfg(windows)]
let c_object_path = operating_dir.join("c_src.obj");
let executable_path = operating_dir.join("a.out");
// TODO: adjust C source code based on locations of things
{
let mut c_src_file = fs::OpenOptions::new()
.create_new(true)
.write(true)
.open(&c_src_file_name)
.context("Failed to open C source code file")?;
c_src_file.write_all(OBJECT_FILE_ENGINE_TEST_C_SOURCE)?;
}
run_c_compile(&operating_dir, &c_src_file_name, &c_object_path)
.context("Failed to compile C source code")?;
LinkCode {
current_dir: operating_dir.clone(),
object_paths: vec![c_object_path, wasm_object_path],
output_path: executable_path.clone(),
..Default::default()
}
.run()
.context("Failed to link objects together")?;
let result = run_code(&operating_dir, &executable_path, &[])
.context("Failed to run generated executable")?;
let result_lines = result.lines().collect::<Vec<&str>>();
assert_eq!(result_lines, vec!["Initializing...", "\"Hello, World\""],);
Ok(())
}

View File

@@ -0,0 +1,162 @@
//! Tests of the `wasmer create-exe` command.
use anyhow::{bail, Context};
use std::fs;
use std::io::prelude::*;
use std::path::PathBuf;
use std::process::Command;
use wasmer_integration_tests_cli::*;
fn create_exe_test_wasm_path() -> String {
format!("{}/{}", ASSET_PATH, "qjs.wasm")
}
const JS_TEST_SRC_CODE: &[u8] =
b"function greet(name) { return JSON.stringify('Hello, ' + name); }; print(greet('World'));\n";
/// Data used to run the `wasmer compile` command.
#[derive(Debug)]
struct WasmerCreateExe {
/// The directory to operate in.
current_dir: PathBuf,
/// Path to wasmer executable used to run the command.
wasmer_path: PathBuf,
/// Path to the Wasm file to compile.
wasm_path: PathBuf,
/// Path to the native executable produced by compiling the Wasm.
native_executable_path: PathBuf,
/// Compiler with which to compile the Wasm.
compiler: Compiler,
}
impl Default for WasmerCreateExe {
fn default() -> Self {
#[cfg(not(windows))]
let native_executable_path = PathBuf::from("wasm.out");
#[cfg(windows)]
let native_executable_path = PathBuf::from("wasm.exe");
Self {
current_dir: std::env::current_dir().unwrap(),
wasmer_path: get_wasmer_path(),
wasm_path: PathBuf::from(create_exe_test_wasm_path()),
native_executable_path,
compiler: Compiler::Cranelift,
}
}
}
impl WasmerCreateExe {
fn run(&self) -> anyhow::Result<()> {
let output = Command::new(&self.wasmer_path)
.current_dir(&self.current_dir)
.arg("create-exe")
.arg(&self.wasm_path.canonicalize()?)
.arg(&self.compiler.to_flag())
.arg("-o")
.arg(&self.native_executable_path)
.output()?;
if !output.status.success() {
bail!(
"wasmer create-exe failed with: stdout: {}\n\nstderr: {}",
std::str::from_utf8(&output.stdout)
.expect("stdout is not utf8! need to handle arbitrary bytes"),
std::str::from_utf8(&output.stderr)
.expect("stderr is not utf8! need to handle arbitrary bytes")
);
}
Ok(())
}
}
#[test]
fn create_exe_works() -> anyhow::Result<()> {
let temp_dir = tempfile::tempdir()?;
let operating_dir: PathBuf = temp_dir.path().to_owned();
let wasm_path = operating_dir.join(create_exe_test_wasm_path());
#[cfg(not(windows))]
let executable_path = operating_dir.join("wasm.out");
#[cfg(windows)]
let executable_path = operating_dir.join("wasm.exe");
WasmerCreateExe {
current_dir: operating_dir.clone(),
wasm_path: wasm_path.clone(),
native_executable_path: executable_path.clone(),
compiler: Compiler::Cranelift,
..Default::default()
}
.run()
.context("Failed to create-exe wasm with Wasmer")?;
let result = run_code(
&operating_dir,
&executable_path,
&["--eval".to_string(), "function greet(name) { return JSON.stringify('Hello, ' + name); }; print(greet('World'));".to_string()],
)
.context("Failed to run generated executable")?;
let result_lines = result.lines().collect::<Vec<&str>>();
assert_eq!(result_lines, vec!["\"Hello, World\""],);
Ok(())
}
#[test]
fn create_exe_works_with_file() -> anyhow::Result<()> {
let temp_dir = tempfile::tempdir()?;
let operating_dir: PathBuf = temp_dir.path().to_owned();
let wasm_path = operating_dir.join(create_exe_test_wasm_path());
#[cfg(not(windows))]
let executable_path = operating_dir.join("wasm.out");
#[cfg(windows)]
let executable_path = operating_dir.join("wasm.exe");
WasmerCreateExe {
current_dir: operating_dir.clone(),
wasm_path: wasm_path.clone(),
native_executable_path: executable_path.clone(),
compiler: Compiler::Cranelift,
..Default::default()
}
.run()
.context("Failed to create-exe wasm with Wasmer")?;
{
let mut f = fs::OpenOptions::new()
.write(true)
.create_new(true)
.open(operating_dir.join("test.js"))?;
f.write_all(JS_TEST_SRC_CODE)?;
}
// test with `--dir`
let result = run_code(
&operating_dir,
&executable_path,
&[
"--dir=.".to_string(),
"--script".to_string(),
"test.js".to_string(),
],
)
.context("Failed to run generated executable")?;
let result_lines = result.lines().collect::<Vec<&str>>();
assert_eq!(result_lines, vec!["\"Hello, World\""],);
// test with `--mapdir`
let result = run_code(
&operating_dir,
&executable_path,
&[
"--mapdir=abc:.".to_string(),
"--script".to_string(),
"abc/test.js".to_string(),
],
)
.context("Failed to run generated executable")?;
let result_lines = result.lines().collect::<Vec<&str>>();
assert_eq!(result_lines, vec!["\"Hello, World\""],);
Ok(())
}

View File

@@ -7,3 +7,7 @@ publish = false
[dependencies] [dependencies]
anyhow = "1.0" anyhow = "1.0"
target-lexicon = "0.10" target-lexicon = "0.10"
[features]
test-native = []
test-jit = []

View File

@@ -47,6 +47,13 @@ pub fn build_ignores_from_textfile(path: PathBuf) -> anyhow::Result<Ignores> {
let file = File::open(path)?; let file = File::open(path)?;
let reader = BufReader::new(file); let reader = BufReader::new(file);
let host = Triple::host().to_string(); let host = Triple::host().to_string();
let engine = if cfg!(feature = "test-native") {
Some("native")
} else if cfg!(feature = "test-jit") {
Some("jit")
} else {
None
};
for line in reader.lines() { for line in reader.lines() {
let line = line.unwrap(); let line = line.unwrap();
// If the line has a `#` we discard all the content that comes after // If the line has a `#` we discard all the content that comes after
@@ -75,8 +82,10 @@ pub fn build_ignores_from_textfile(path: PathBuf) -> anyhow::Result<Ignores> {
} }
// We skip the ignore if doesn't apply to the current // We skip the ignore if doesn't apply to the current
// host target // host target or engine
if target.map(|t| !host.contains(&t)).unwrap_or(false) { if target.clone().map(|t| !host.contains(&t)).unwrap_or(false)
&& target.clone() != engine.map(str::to_string)
{
continue; continue;
} }

View File

@@ -23,7 +23,6 @@ typetag = "0.1"
[features] [features]
default = ["wat"] default = ["wat"]
wat = ["wasmer/wat"] wat = ["wasmer/wat"]
test-no-traps = []
[badges] [badges]
maintenance = { status = "actively-developed" } maintenance = { status = "actively-developed" }

View File

@@ -18,12 +18,18 @@ pub struct Wast {
instances: HashMap<String, Instance>, instances: HashMap<String, Instance>,
/// Allowed failures (ideally this should be empty) /// Allowed failures (ideally this should be empty)
allowed_instantiation_failures: HashSet<String>, allowed_instantiation_failures: HashSet<String>,
/// If the (expected from .wast, actual) message pair is in this list,
/// treat the strings as matching.
match_trap_messages: HashMap<String, String>,
/// If the current module was an allowed failure, we allow test to fail /// If the current module was an allowed failure, we allow test to fail
current_is_allowed_failure: bool, current_is_allowed_failure: bool,
/// The wasm Store /// The wasm Store
store: Store, store: Store,
/// A flag indicating if Wast tests should stop as soon as one test fails. /// A flag indicating if Wast tests should stop as soon as one test fails.
pub fail_fast: bool, pub fail_fast: bool,
/// A flag indicating that assert_trap and assert_exhaustion should be skipped.
/// See https://github.com/wasmerio/wasmer/issues/1550 for more info
disable_assert_trap_exhaustion: bool,
} }
impl Wast { impl Wast {
@@ -34,13 +40,15 @@ impl Wast {
store, store,
import_object, import_object,
allowed_instantiation_failures: HashSet::new(), allowed_instantiation_failures: HashSet::new(),
match_trap_messages: HashMap::new(),
current_is_allowed_failure: false, current_is_allowed_failure: false,
instances: HashMap::new(), instances: HashMap::new(),
fail_fast: true, fail_fast: true,
disable_assert_trap_exhaustion: false,
} }
} }
/// A list of instantiation failures to allow /// A list of instantiation failures to allow.
pub fn allow_instantiation_failures(&mut self, failures: &[&str]) { pub fn allow_instantiation_failures(&mut self, failures: &[&str]) {
for &failure_str in failures.iter() { for &failure_str in failures.iter() {
self.allowed_instantiation_failures self.allowed_instantiation_failures
@@ -48,6 +56,17 @@ impl Wast {
} }
} }
/// A list of alternative messages to permit for a trap failure.
pub fn allow_trap_message(&mut self, expected: &str, allowed: &str) {
self.match_trap_messages
.insert(expected.into(), allowed.into());
}
/// Do not run any code in assert_trap or assert_exhaustion.
pub fn disable_assert_and_exhaustion(&mut self) {
self.disable_assert_trap_exhaustion = true;
}
/// Construct a new instance of `Wast` with the spectests imports. /// Construct a new instance of `Wast` with the spectests imports.
pub fn new_with_spectest(store: Store) -> Self { pub fn new_with_spectest(store: Store) -> Self {
let import_object = spectest_importobject(&store); let import_object = spectest_importobject(&store);
@@ -123,7 +142,7 @@ impl Wast {
Ok(values) => bail!("expected trap, got {:?}", values), Ok(values) => bail!("expected trap, got {:?}", values),
Err(t) => format!("{}", t), Err(t) => format!("{}", t),
}; };
if Self::matches_message_assert_trap(expected, &actual) { if self.matches_message_assert_trap(expected, &actual) {
return Ok(()); return Ok(());
} }
bail!("expected '{}', got '{}'", expected, actual) bail!("expected '{}', got '{}'", expected, actual)
@@ -155,27 +174,26 @@ impl Wast {
let result = self.perform_execute(exec); let result = self.perform_execute(exec);
self.assert_return(result, &results)?; self.assert_return(result, &results)?;
} }
#[cfg(not(feature = "test-no-traps"))]
AssertTrap { AssertTrap {
span: _, span: _,
exec, exec,
message, message,
} => { } => {
let result = self.perform_execute(exec); if !self.disable_assert_trap_exhaustion {
self.assert_trap(result, message)?; let result = self.perform_execute(exec);
self.assert_trap(result, message)?;
}
} }
#[cfg(not(feature = "test-no-traps"))]
AssertExhaustion { AssertExhaustion {
span: _, span: _,
call, call,
message, message,
} => { } => {
let result = self.perform_invoke(call); if !self.disable_assert_trap_exhaustion {
self.assert_trap(result, message)?; let result = self.perform_invoke(call);
self.assert_trap(result, message)?;
}
} }
// See https://github.com/wasmerio/wasmer/issues/1550 for more info
#[cfg(feature = "test-no-traps")]
AssertTrap { .. } | AssertExhaustion { .. } => {}
AssertInvalid { AssertInvalid {
span: _, span: _,
mut module, mut module,
@@ -406,12 +424,13 @@ impl Wast {
} }
// Checks if the `assert_trap` message matches the expected one // Checks if the `assert_trap` message matches the expected one
fn matches_message_assert_trap(expected: &str, actual: &str) -> bool { fn matches_message_assert_trap(&self, expected: &str, actual: &str) -> bool {
actual.contains(expected) actual.contains(expected)
// `bulk-memory-operations/bulk.wast` checks for a message that || self
// specifies which element is uninitialized, but our traps don't .match_trap_messages
// shepherd that information out. .get(expected)
|| (expected.contains("uninitialized element 2") && actual.contains("uninitialized element")) .map(|alternative| actual.contains(alternative))
.unwrap_or(false)
} }
} }