mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-12 05:18:43 +00:00
Merge branch 'master' into feat-c-api-update-wasm-h
This commit is contained in:
@@ -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"
|
||||||
]
|
]
|
||||||
|
|||||||
6
.github/workflows/benchmark.yaml
vendored
6
.github/workflows/benchmark.yaml
vendored
@@ -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:
|
||||||
|
|||||||
4
.github/workflows/coverage.yaml
vendored
4
.github/workflows/coverage.yaml
vendored
@@ -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
|
||||||
|
|||||||
42
.github/workflows/main.yaml
vendored
42
.github/workflows/main.yaml
vendored
@@ -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
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
[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
|
||||||
|
|
||||||
[report]
|
[report]
|
||||||
out = ["Xml"]
|
out = ["Xml"]
|
||||||
|
|||||||
80
CHANGELOG.md
80
CHANGELOG.md
@@ -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
1
Cargo.lock
generated
@@ -2215,6 +2215,7 @@ dependencies = [
|
|||||||
"fern",
|
"fern",
|
||||||
"log",
|
"log",
|
||||||
"structopt",
|
"structopt",
|
||||||
|
"tempfile",
|
||||||
"wasmer",
|
"wasmer",
|
||||||
"wasmer-cache",
|
"wasmer-cache",
|
||||||
"wasmer-compiler",
|
"wasmer-compiler",
|
||||||
|
|||||||
15
Cargo.toml
15
Cargo.toml
@@ -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"
|
||||||
|
|||||||
40
Makefile
40
Makefile
@@ -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 #
|
||||||
#################
|
#################
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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::*};
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
306
lib/cli/src/commands/create_exe.rs
Normal file
306
lib/cli/src/commands/create_exe.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
172
lib/cli/src/commands/wasmer_create_exe_main.c
Normal file
172
lib/cli/src/commands/wasmer_create_exe_main.c
Normal 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;
|
||||||
|
}
|
||||||
@@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 }
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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)"#)?;
|
||||||
|
|||||||
@@ -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())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(&[
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
32
tests/integration/cli/src/assets.rs
Normal file
32
tests/integration/cli/src/assets.rs
Normal 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()))
|
||||||
|
}
|
||||||
@@ -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(())
|
|
||||||
}
|
|
||||||
@@ -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::*;
|
||||||
|
|||||||
69
tests/integration/cli/src/link_code.rs
Normal file
69
tests/integration/cli/src/link_code.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
62
tests/integration/cli/src/util.rs
Normal file
62
tests/integration/cli/src/util.rs
Normal 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())
|
||||||
|
}
|
||||||
173
tests/integration/cli/tests/compile.rs
Normal file
173
tests/integration/cli/tests/compile.rs
Normal 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(())
|
||||||
|
}
|
||||||
162
tests/integration/cli/tests/create_exe.rs
Normal file
162
tests/integration/cli/tests/create_exe.rs
Normal 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(())
|
||||||
|
}
|
||||||
@@ -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 = []
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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" }
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user