mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-22 13:11:32 +00:00
Merge remote-tracking branch 'origin/master' into feature/remote-code-load
This commit is contained in:
10
CHANGELOG.md
10
CHANGELOG.md
@ -5,6 +5,16 @@ All PRs to the Wasmer repository must add to this file.
|
||||
Blocks of changes will separated by version increments.
|
||||
|
||||
## **[Unreleased]**
|
||||
|
||||
- [#269](https://github.com/wasmerio/wasmer/pull/269) Add better runtime docs
|
||||
- [#432](https://github.com/wasmerio/wasmer/pull/432) Fix returned value of `wasmer_last_error_message` in the runtime C API
|
||||
- [#429](https://github.com/wasmerio/wasmer/pull/429) Get wasi::path_filestat_get working for some programs; misc. minor WASI FS improvements
|
||||
- [#413](https://github.com/wasmerio/wasmer/pull/413) Update LLVM backend to use new parser codegen traits
|
||||
|
||||
## 0.4.1 - 2018-05-06
|
||||
|
||||
- [#426](https://github.com/wasmerio/wasmer/pull/426) Update wapm-cli submodule, bump version to 0.4.1
|
||||
- [#422](https://github.com/wasmerio/wasmer/pull/422) Improved Emscripten functions to run optipng and pngquant compiled to wasm
|
||||
- [#409](https://github.com/wasmerio/wasmer/pull/409) Improved Emscripten functions to run JavascriptCore compiled to wasm
|
||||
- [#399](https://github.com/wasmerio/wasmer/pull/399) Add example of using a plugin extended from WASI
|
||||
- [#397](https://github.com/wasmerio/wasmer/pull/397) Fix WASI fs abstraction to work on Windows
|
||||
|
88
Cargo.lock
generated
88
Cargo.lock
generated
@ -2256,7 +2256,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasmer"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2266,20 +2266,20 @@ dependencies = [
|
||||
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasmer-clif-backend 0.3.0",
|
||||
"wasmer-emscripten 0.3.0",
|
||||
"wasmer-llvm-backend 0.3.0",
|
||||
"wasmer-middleware-common 0.3.0",
|
||||
"wasmer-runtime 0.3.0",
|
||||
"wasmer-runtime-abi 0.3.0",
|
||||
"wasmer-runtime-core 0.3.0",
|
||||
"wasmer-singlepass-backend 0.3.0",
|
||||
"wasmer-wasi 0.3.0",
|
||||
"wasmer-clif-backend 0.4.1",
|
||||
"wasmer-emscripten 0.4.1",
|
||||
"wasmer-llvm-backend 0.4.1",
|
||||
"wasmer-middleware-common 0.4.1",
|
||||
"wasmer-runtime 0.4.1",
|
||||
"wasmer-runtime-abi 0.4.1",
|
||||
"wasmer-runtime-core 0.4.1",
|
||||
"wasmer-singlepass-backend 0.4.1",
|
||||
"wasmer-wasi 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmer-clif-backend"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cranelift-codegen 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2295,15 +2295,15 @@ dependencies = [
|
||||
"serde_bytes 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"target-lexicon 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasmer-runtime-core 0.3.0",
|
||||
"wasmer-win-exception-handler 0.3.0",
|
||||
"wasmer-runtime-core 0.4.1",
|
||||
"wasmer-win-exception-handler 0.4.1",
|
||||
"wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmer-emscripten"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2312,15 +2312,15 @@ dependencies = [
|
||||
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasmer-clif-backend 0.3.0",
|
||||
"wasmer-llvm-backend 0.3.0",
|
||||
"wasmer-runtime-core 0.3.0",
|
||||
"wasmer-singlepass-backend 0.3.0",
|
||||
"wasmer-clif-backend 0.4.1",
|
||||
"wasmer-llvm-backend 0.4.1",
|
||||
"wasmer-runtime-core 0.4.1",
|
||||
"wasmer-singlepass-backend 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmer-llvm-backend"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"capstone 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2335,42 +2335,42 @@ dependencies = [
|
||||
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasmer-runtime-core 0.3.0",
|
||||
"wasmer-runtime-core 0.4.1",
|
||||
"wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmer-middleware-common"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"wasmer-runtime-core 0.3.0",
|
||||
"wasmer-runtime-core 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmer-runtime"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"criterion 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasmer-clif-backend 0.3.0",
|
||||
"wasmer-llvm-backend 0.3.0",
|
||||
"wasmer-runtime-core 0.3.0",
|
||||
"wasmer-singlepass-backend 0.3.0",
|
||||
"wasmer-clif-backend 0.4.1",
|
||||
"wasmer-llvm-backend 0.4.1",
|
||||
"wasmer-runtime-core 0.4.1",
|
||||
"wasmer-singlepass-backend 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmer-runtime-abi"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tar 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasmer-runtime-core 0.3.0",
|
||||
"wasmer-runtime-core 0.4.1",
|
||||
"wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"zbox 0.6.1 (git+https://github.com/wasmerio/zbox?branch=bundle-libsodium)",
|
||||
"zstd 0.4.22+zstd.1.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2378,17 +2378,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasmer-runtime-c-api"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"cbindgen 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasmer-runtime 0.3.0",
|
||||
"wasmer-runtime-core 0.3.0",
|
||||
"wasmer-runtime 0.4.1",
|
||||
"wasmer-runtime-core 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmer-runtime-core"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"blake2b_simd 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2414,7 +2414,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasmer-singlepass-backend"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dynasm 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2424,24 +2424,24 @@ dependencies = [
|
||||
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasmer-runtime-core 0.3.0",
|
||||
"wasmer-runtime-core 0.4.1",
|
||||
"wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmer-spectests"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasmer-clif-backend 0.3.0",
|
||||
"wasmer-llvm-backend 0.3.0",
|
||||
"wasmer-runtime-core 0.3.0",
|
||||
"wasmer-singlepass-backend 0.3.0",
|
||||
"wasmer-clif-backend 0.4.1",
|
||||
"wasmer-llvm-backend 0.4.1",
|
||||
"wasmer-runtime-core 0.4.1",
|
||||
"wasmer-singlepass-backend 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmer-wasi"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"generational-arena 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2449,18 +2449,18 @@ dependencies = [
|
||||
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasmer-runtime-core 0.3.0",
|
||||
"wasmer-runtime-core 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmer-win-exception-handler"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"bindgen 0.46.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cmake 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasmer-runtime-core 0.3.0",
|
||||
"wasmer-runtime-core 0.4.1",
|
||||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
edition = "2018"
|
||||
repository = "https://github.com/wasmerio/wasmer"
|
||||
@ -45,7 +45,8 @@ rustc_version = "0.2.3"
|
||||
|
||||
[features]
|
||||
default = ["fast-tests", "wasi"]
|
||||
debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"]
|
||||
debug = ["wasmer-runtime-core/debug"]
|
||||
extra-debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"]
|
||||
# This feature will allow cargo test to run much faster
|
||||
fast-tests = []
|
||||
"backend:llvm" = ["wasmer-llvm-backend"]
|
||||
|
3
Makefile
3
Makefile
@ -84,5 +84,8 @@ production-release:
|
||||
debug-release:
|
||||
cargo build --release --features "debug"
|
||||
|
||||
extra-debug-release:
|
||||
cargo build --release --features "extra-debug"
|
||||
|
||||
publish-release:
|
||||
ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete ${VERSION} ./artifacts/
|
||||
|
24
README.md
24
README.md
@ -27,10 +27,12 @@ curl https://get.wasmer.io -sSfL | sh
|
||||
```
|
||||
|
||||
Wasmer runtime can also be embedded in different languages, so you can use WebAssembly anywhere ✨:
|
||||
|
||||
* [**Rust**](https://github.com/wasmerio/wasmer-rust-example)
|
||||
* [**C/C++**](https://github.com/wasmerio/wasmer-c-api)
|
||||
* [**PHP**](https://github.com/wasmerio/php-ext-wasm)
|
||||
* [**Python**](https://github.com/wasmerio/python-ext-wasm)
|
||||
* [**🐘 PHP**](https://github.com/wasmerio/php-ext-wasm)
|
||||
* [**🐍 Python**](https://github.com/wasmerio/python-ext-wasm)
|
||||
* [**💎 Ruby**](https://github.com/wasmerio/ruby-ext-wasm)
|
||||
|
||||
### Usage
|
||||
|
||||
@ -53,6 +55,24 @@ wasmer run examples/sqlite.wasm
|
||||
wasmer run examples/nginx/nginx.wasm -- -p examples/nginx -c nginx.conf
|
||||
```
|
||||
|
||||
#### With WAPM
|
||||
|
||||
Installing Wasmer through `wasmer.io` includes
|
||||
[wapm](https://github.com/wasmerio/wapm-cli), the WebAssembly package manager.
|
||||
|
||||
Wapm allows you to easily download, run, and distribute WebAssembly binaries.
|
||||
|
||||
```sh
|
||||
# Install cowsay globally
|
||||
wapm install -g cowsay
|
||||
|
||||
# Run cowsay
|
||||
wapm run cowsay "Hello, world!"
|
||||
```
|
||||
|
||||
For more information about wapm, check out the [website](https://www.wapm.io)
|
||||
and this [example program](https://github.com/wapm-packages/rust-wasi-example).
|
||||
|
||||
## Code Structure
|
||||
|
||||
Wasmer is structured into different directories:
|
||||
|
9
examples/README.md
Normal file
9
examples/README.md
Normal file
@ -0,0 +1,9 @@
|
||||
# WebAssembly Examples
|
||||
|
||||
In this directory you can find a set of different examples that can run on the Wasmer WebAssembly runtime:
|
||||
|
||||
* Cowsay (WASI ABI) [[source-code](https://github.com/wapm-packages/cowsay)] [[wapm-package](https://wapm.io/package/cowsay)]
|
||||
* Nginx (Emscripten ABI) [[source-code](https://github.com/wapm-packages/nginx)] [[wapm-package](https://wapm.io/package/nginx)]
|
||||
* Lua (Emscripten ABI) [[source-code](https://github.com/wapm-packages/lua)] [[wapm-package](https://wapm.io/package/lua)]
|
||||
* PHP (Emscripten ABI) [[source-code](https://github.com/wapm-packages/php)] [[wapm-package](https://wapm.io/package/php)]
|
||||
* SQLite (Emscripten ABI) [[source-code](https://github.com/wapm-packages/sqlite)] [[wapm-package](https://wapm.io/package/sqlite)]
|
18
install.sh
18
install.sh
@ -130,11 +130,11 @@ wasmer_detect_profile() {
|
||||
wasmer_link() {
|
||||
printf "$cyan> Adding to bash profile...$reset\n"
|
||||
WASMER_PROFILE="$(wasmer_detect_profile)"
|
||||
LOAD_STR="\n# Wasmer\nexport WASMER_DIR=\"\$HOME/.wasmer\"\n[ -s \"\$WASMER_DIR/wasmer.sh\" ] && source \"\$WASMER_DIR/wasmer.sh\" # This loads wasmer\n"
|
||||
SOURCE_STR="# Wasmer config\nexport WASMER_DIR=\"\$HOME/.wasmer\"\nexport WASMER_CACHE_DIR=\"\$WASMER_DIR/cache\"\nexport PATH=\"\$HOME/.wasmer/bin:\$HOME/.wasmer/globals/wapm_packages/.bin:\$PATH\"\n"
|
||||
LOAD_STR="\n# Wasmer\nexport WASMER_DIR=\"$INSTALL_DIRECTORY\"\n[ -s \"\$WASMER_DIR/wasmer.sh\" ] && source \"\$WASMER_DIR/wasmer.sh\" # This loads wasmer\n"
|
||||
SOURCE_STR="# Wasmer config\nexport WASMER_DIR=\"$INSTALL_DIRECTORY\"\nexport WASMER_CACHE_DIR=\"\$WASMER_DIR/cache\"\nexport PATH=\"\$WASMER_DIR/bin:\$WASMER_DIR/globals/wapm_packages/.bin:\$PATH\"\n"
|
||||
|
||||
# We create the wasmer.sh file
|
||||
printf "$SOURCE_STR" > "$HOME/.wasmer/wasmer.sh"
|
||||
printf "$SOURCE_STR" > "$INSTALL_DIRECTORY/wasmer.sh"
|
||||
|
||||
if [ -z "${WASMER_PROFILE-}" ] ; then
|
||||
printf "${red}Profile not found. Tried:\n* ${WASMER_PROFILE} (as defined in \$PROFILE)\n* ~/.bashrc\n* ~/.bash_profile\n* ~/.zshrc\n* ~/.profile.\n"
|
||||
@ -156,12 +156,12 @@ wasmer_link() {
|
||||
echo "If this isn't the profile of your current shell then please add the following to your correct profile:"
|
||||
printf "$LOAD_STR$reset\n"
|
||||
|
||||
version=`$HOME/.wasmer/bin/wasmer --version` || (
|
||||
version=`$INSTALL_DIRECTORY/bin/wasmer --version` || (
|
||||
printf "$red> wasmer was installed, but doesn't seem to be working :($reset\n"
|
||||
exit 1;
|
||||
)
|
||||
|
||||
printf "$green> Successfully installed $version!\n\n${reset}If you want to have the command available now please execute:\nsource $HOME/.wasmer/wasmer.sh$reset\n"
|
||||
printf "$green> Successfully installed $version!\n\n${reset}If you want to have the command available now please execute:\nsource $INSTALL_DIRECTORY/wasmer.sh$reset\n"
|
||||
printf "\nOtherwise, wasmer and wapm will be available the next time you open the terminal.\n"
|
||||
echo "Note: during the alpha release of wapm, telemetry is enabled by default; if you would like to opt out, run \`wapm config set telemetry.enabled false\`."
|
||||
echo "If you notice anything wrong or have any issues, please file a bug at https://github.com/wasmerio/wapm-cli :)"
|
||||
@ -257,7 +257,7 @@ wasmer_install() {
|
||||
|
||||
"
|
||||
fi
|
||||
# if [ -d "$HOME/.wasmer" ]; then
|
||||
# if [ -d "$INSTALL_DIRECTORY" ]; then
|
||||
# if which wasmer; then
|
||||
# local latest_url
|
||||
# local specified_version
|
||||
@ -286,11 +286,11 @@ wasmer_install() {
|
||||
# exit 0
|
||||
# else
|
||||
# printf "$yellow> $wasmer_alt_version is already installed, Specified version: $specified_version.$reset\n"
|
||||
# rm -rf "$HOME/.wasmer"
|
||||
# rm -rf "$INSTALL_DIRECTORY"
|
||||
# fi
|
||||
# else
|
||||
# printf "$red> $HOME/.wasmer already exists, possibly from a past Wasmer install.$reset\n"
|
||||
# printf "$red> Remove it (rm -rf $HOME/.wasmer) and run this script again.$reset\n"
|
||||
# printf "$red> $INSTALL_DIRECTORY already exists, possibly from a past Wasmer install.$reset\n"
|
||||
# printf "$red> Remove it (rm -rf $INSTALL_DIRECTORY) and run this script again.$reset\n"
|
||||
# exit 0
|
||||
# fi
|
||||
# fi
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-clif-backend"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
description = "Wasmer runtime Cranelift compiler backend"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
@ -8,7 +8,7 @@ repository = "https://github.com/wasmerio/wasmer"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.3.0" }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" }
|
||||
cranelift-native = "0.30.0"
|
||||
cranelift-codegen = "0.30.0"
|
||||
cranelift-entity = "0.30.0"
|
||||
@ -33,7 +33,7 @@ version = "0.0.7"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] }
|
||||
wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.3.0" }
|
||||
wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.4.1" }
|
||||
|
||||
[features]
|
||||
debug = ["wasmer-runtime-core/debug"]
|
||||
|
@ -1,3 +1,5 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
mod cache;
|
||||
mod func_env;
|
||||
mod libcalls;
|
||||
|
@ -1,5 +1,5 @@
|
||||
//! Installing signal handlers allows us to handle traps and out-of-bounds memory
|
||||
//! accesses that occur when runniing webassembly.
|
||||
//! accesses that occur when running WebAssembly.
|
||||
//!
|
||||
//! This code is inspired by: https://github.com/pepyakin/wasmtime/commit/625a2b6c0815b21996e111da51b9664feb174622
|
||||
//!
|
||||
@ -107,7 +107,7 @@ pub fn call_protected<T>(
|
||||
Ok(SIGSEGV) => "segmentation violation",
|
||||
Ok(SIGBUS) => "bus error",
|
||||
Err(_) => "error while getting the Signal",
|
||||
_ => "unkown trapped signal",
|
||||
_ => "unknown trapped signal",
|
||||
};
|
||||
// When the trap-handler is fully implemented, this will return more information.
|
||||
let s = format!("unknown trap at {:p} - {}", faulting_addr, signal);
|
||||
|
@ -4,7 +4,6 @@ use crate::trampoline::Trampoline;
|
||||
use std::cell::Cell;
|
||||
use std::ffi::c_void;
|
||||
use std::ptr::{self, NonNull};
|
||||
use wasmer_runtime_core::error::{RuntimeError, RuntimeResult};
|
||||
use wasmer_runtime_core::typed_func::WasmTrapInfo;
|
||||
use wasmer_runtime_core::vm::Ctx;
|
||||
use wasmer_runtime_core::vm::Func;
|
||||
@ -12,10 +11,14 @@ use wasmer_win_exception_handler::CallProtectedData;
|
||||
pub use wasmer_win_exception_handler::_call_protected;
|
||||
use winapi::shared::minwindef::DWORD;
|
||||
use winapi::um::minwinbase::{
|
||||
EXCEPTION_ACCESS_VIOLATION, EXCEPTION_FLT_DENORMAL_OPERAND, EXCEPTION_FLT_DIVIDE_BY_ZERO,
|
||||
EXCEPTION_ACCESS_VIOLATION, EXCEPTION_ARRAY_BOUNDS_EXCEEDED, EXCEPTION_BREAKPOINT,
|
||||
EXCEPTION_DATATYPE_MISALIGNMENT, EXCEPTION_FLT_DENORMAL_OPERAND, EXCEPTION_FLT_DIVIDE_BY_ZERO,
|
||||
EXCEPTION_FLT_INEXACT_RESULT, EXCEPTION_FLT_INVALID_OPERATION, EXCEPTION_FLT_OVERFLOW,
|
||||
EXCEPTION_FLT_STACK_CHECK, EXCEPTION_FLT_UNDERFLOW, EXCEPTION_ILLEGAL_INSTRUCTION,
|
||||
EXCEPTION_INT_DIVIDE_BY_ZERO, EXCEPTION_INT_OVERFLOW, EXCEPTION_STACK_OVERFLOW,
|
||||
EXCEPTION_FLT_STACK_CHECK, EXCEPTION_FLT_UNDERFLOW, EXCEPTION_GUARD_PAGE,
|
||||
EXCEPTION_ILLEGAL_INSTRUCTION, EXCEPTION_INT_DIVIDE_BY_ZERO, EXCEPTION_INT_OVERFLOW,
|
||||
EXCEPTION_INVALID_HANDLE, EXCEPTION_IN_PAGE_ERROR, EXCEPTION_NONCONTINUABLE_EXCEPTION,
|
||||
EXCEPTION_POSSIBLE_DEADLOCK, EXCEPTION_PRIV_INSTRUCTION, EXCEPTION_SINGLE_STEP,
|
||||
EXCEPTION_STACK_OVERFLOW,
|
||||
};
|
||||
|
||||
thread_local! {
|
||||
@ -43,7 +46,7 @@ pub fn call_protected(
|
||||
}
|
||||
|
||||
let CallProtectedData {
|
||||
code: signum,
|
||||
code,
|
||||
exception_address,
|
||||
instruction_pointer,
|
||||
} = result.unwrap_err();
|
||||
@ -53,7 +56,7 @@ pub fn call_protected(
|
||||
srcloc: _,
|
||||
}) = handler_data.lookup(instruction_pointer as _)
|
||||
{
|
||||
Err(CallProtError::Trap(match signum as DWORD {
|
||||
Err(CallProtError::Trap(match code as DWORD {
|
||||
EXCEPTION_ACCESS_VIOLATION => WasmTrapInfo::MemoryOutOfBounds,
|
||||
EXCEPTION_ILLEGAL_INSTRUCTION => match trapcode {
|
||||
TrapCode::BadSignature => WasmTrapInfo::IncorrectCallIndirectSignature,
|
||||
@ -70,7 +73,7 @@ pub fn call_protected(
|
||||
_ => WasmTrapInfo::Unknown,
|
||||
}))
|
||||
} else {
|
||||
let signal = match signum as DWORD {
|
||||
let signal = match code as DWORD {
|
||||
EXCEPTION_FLT_DENORMAL_OPERAND
|
||||
| EXCEPTION_FLT_DIVIDE_BY_ZERO
|
||||
| EXCEPTION_FLT_INEXACT_RESULT
|
||||
@ -80,10 +83,26 @@ pub fn call_protected(
|
||||
| EXCEPTION_FLT_UNDERFLOW => "floating-point exception",
|
||||
EXCEPTION_ILLEGAL_INSTRUCTION => "illegal instruction",
|
||||
EXCEPTION_ACCESS_VIOLATION => "segmentation violation",
|
||||
_ => "unkown trapped signal",
|
||||
EXCEPTION_DATATYPE_MISALIGNMENT => "datatype misalignment",
|
||||
EXCEPTION_BREAKPOINT => "breakpoint",
|
||||
EXCEPTION_SINGLE_STEP => "single step",
|
||||
EXCEPTION_ARRAY_BOUNDS_EXCEEDED => "array bounds exceeded",
|
||||
EXCEPTION_INT_DIVIDE_BY_ZERO => "int div by zero",
|
||||
EXCEPTION_INT_OVERFLOW => "int overflow",
|
||||
EXCEPTION_PRIV_INSTRUCTION => "priv instruction",
|
||||
EXCEPTION_IN_PAGE_ERROR => "in page error",
|
||||
EXCEPTION_NONCONTINUABLE_EXCEPTION => "non continuable exception",
|
||||
EXCEPTION_STACK_OVERFLOW => "stack overflow",
|
||||
EXCEPTION_GUARD_PAGE => "guard page",
|
||||
EXCEPTION_INVALID_HANDLE => "invalid handle",
|
||||
EXCEPTION_POSSIBLE_DEADLOCK => "possible deadlock",
|
||||
_ => "unknown exception code",
|
||||
};
|
||||
|
||||
let s = format!("unknown trap at {} - {}", exception_address, signal);
|
||||
let s = format!(
|
||||
"unhandled trap at {:x} - code #{:x}: {}",
|
||||
exception_address, code, signal,
|
||||
);
|
||||
|
||||
Err(CallProtError::Error(Box::new(s)))
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-emscripten"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
description = "Wasmer runtime emscripten implementation library"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
@ -9,14 +9,14 @@ edition = "2018"
|
||||
build = "build/mod.rs"
|
||||
|
||||
[dependencies]
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.3.0" }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" }
|
||||
lazy_static = "1.2.0"
|
||||
libc = "0.2.49"
|
||||
byteorder = "1"
|
||||
time = "0.1.41"
|
||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.3.0" }
|
||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.3.0", optional = true }
|
||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.3.0", optional = true }
|
||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.4.1" }
|
||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.1", optional = true }
|
||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.4.1", optional = true }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
rand = "0.6"
|
||||
|
@ -55,7 +55,7 @@ pub fn _dladdr(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 {
|
||||
0
|
||||
}
|
||||
pub fn _pthread_attr_init(_ctx: &mut Ctx, _a: i32) -> i32 {
|
||||
debug!("emscripten::_pthread_attr_init");
|
||||
debug!("emscripten::_pthread_attr_init({})", _a);
|
||||
0
|
||||
}
|
||||
pub fn _pthread_attr_destroy(_ctx: &mut Ctx, _a: i32) -> i32 {
|
||||
@ -68,7 +68,10 @@ pub fn _pthread_attr_getstack(
|
||||
_stacksize: i32,
|
||||
_other: i32,
|
||||
) -> i32 {
|
||||
debug!("emscripten::_pthread_attr_getstack");
|
||||
debug!(
|
||||
"emscripten::_pthread_attr_getstack({}, {}, {})",
|
||||
_stackaddr, _stacksize, _other
|
||||
);
|
||||
// TODO: Translate from Emscripten
|
||||
// HEAP32[stackaddr >> 2] = STACK_BASE;
|
||||
// HEAP32[stacksize >> 2] = TOTAL_STACK;
|
||||
@ -87,7 +90,7 @@ pub fn _pthread_getspecific(_ctx: &mut Ctx, _a: i32) -> i32 {
|
||||
0
|
||||
}
|
||||
pub fn _pthread_getattr_np(_ctx: &mut Ctx, _thread: i32, _attr: i32) -> i32 {
|
||||
debug!("emscripten::_pthread_getattr_np");
|
||||
debug!("emscripten::_pthread_getattr_np({}, {})", _thread, _attr);
|
||||
0
|
||||
}
|
||||
pub fn _pthread_setspecific(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 {
|
||||
@ -732,6 +735,10 @@ pub fn invoke_vj(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) {
|
||||
panic!("dyn_call_vj is set to None");
|
||||
}
|
||||
}
|
||||
pub fn invoke_vjji(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32, a5: i32) {
|
||||
debug!("emscripten::invoke_vjji");
|
||||
invoke_no_return!(ctx, dyn_call_vjji, index, a1, a2, a3, a4, a5)
|
||||
}
|
||||
pub fn invoke_vij(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) {
|
||||
debug!("emscripten::invoke_vij");
|
||||
if let Some(dyn_call_vij) = &get_emscripten_data(ctx).dyn_call_vij {
|
||||
|
40
lib/emscripten/src/env/mod.rs
vendored
40
lib/emscripten/src/env/mod.rs
vendored
@ -60,21 +60,51 @@ pub fn ___build_environment(ctx: &mut Ctx, environ: c_int) {
|
||||
const MAX_ENV_VALUES: u32 = 64;
|
||||
const TOTAL_ENV_SIZE: u32 = 1024;
|
||||
let environment = emscripten_memory_pointer!(ctx.memory(0), environ) as *mut c_int;
|
||||
unsafe {
|
||||
let (mut pool_offset, env_ptr, mut pool_ptr) = unsafe {
|
||||
let (pool_offset, _pool_slice): (u32, &mut [u8]) =
|
||||
allocate_on_stack(ctx, TOTAL_ENV_SIZE as u32);
|
||||
let (env_offset, _env_slice): (u32, &mut [u8]) =
|
||||
allocate_on_stack(ctx, (MAX_ENV_VALUES * 4) as u32);
|
||||
let env_ptr = emscripten_memory_pointer!(ctx.memory(0), env_offset) as *mut c_int;
|
||||
let mut _pool_ptr = emscripten_memory_pointer!(ctx.memory(0), pool_offset) as *mut c_int;
|
||||
let pool_ptr = emscripten_memory_pointer!(ctx.memory(0), pool_offset) as *mut u8;
|
||||
*env_ptr = pool_offset as i32;
|
||||
*environment = env_offset as i32;
|
||||
|
||||
// *env_ptr = 0;
|
||||
(pool_offset, env_ptr, pool_ptr)
|
||||
};
|
||||
// unsafe {
|
||||
|
||||
// *env_ptr = 0;
|
||||
// };
|
||||
let default_vars = vec![
|
||||
["USER", "web_user"],
|
||||
["LOGNAME", "web_user"],
|
||||
["PATH", "/"],
|
||||
["PWD", "/"],
|
||||
["HOME", "/home/web_user"],
|
||||
["LANG", "C.UTF-8"],
|
||||
["_", "thisProgram"],
|
||||
];
|
||||
let mut strings = vec![];
|
||||
let mut total_size = 0;
|
||||
for [key, val] in &default_vars {
|
||||
let line = key.to_string() + "=" + val;
|
||||
total_size += line.len();
|
||||
strings.push(line);
|
||||
}
|
||||
if total_size as u32 > TOTAL_ENV_SIZE {
|
||||
panic!("Environment size exceeded TOTAL_ENV_SIZE!");
|
||||
}
|
||||
unsafe {
|
||||
for (i, s) in strings.iter().enumerate() {
|
||||
for (j, c) in s.chars().enumerate() {
|
||||
debug_assert!(c < u8::max_value() as char);
|
||||
*pool_ptr.add(j) = c as u8;
|
||||
}
|
||||
*env_ptr.add(i * 4) = pool_offset as i32;
|
||||
pool_offset += s.len() as u32 + 1;
|
||||
pool_ptr = pool_ptr.add(s.len() + 1);
|
||||
}
|
||||
*env_ptr.add(strings.len() * 4) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ___assert_fail(_ctx: &mut Ctx, _a: c_int, _b: c_int, _c: c_int, _d: c_int) {
|
||||
|
@ -7,7 +7,7 @@ use wasmer_runtime_core::vm::Ctx;
|
||||
/// setjmp
|
||||
pub fn __setjmp(ctx: &mut Ctx, _env_addr: u32) -> c_int {
|
||||
debug!("emscripten::__setjmp (setjmp)");
|
||||
abort_with_message(ctx, "missing function: _longjmp");
|
||||
abort_with_message(ctx, "missing function: _setjmp");
|
||||
unreachable!()
|
||||
// unsafe {
|
||||
// // Rather than using the env as the holder of the jump buffer pointer,
|
||||
|
@ -1,3 +1,5 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate wasmer_runtime_core;
|
||||
|
||||
@ -50,7 +52,7 @@ mod varargs;
|
||||
|
||||
pub use self::storage::{align_memory, static_alloc};
|
||||
pub use self::utils::{
|
||||
allocate_cstr_on_stack, allocate_on_stack, get_emscripten_memory_size,
|
||||
allocate_cstr_on_stack, allocate_on_stack, get_emscripten_memory_size, get_emscripten_metadata,
|
||||
get_emscripten_table_size, is_emscripten_module,
|
||||
};
|
||||
|
||||
@ -127,6 +129,7 @@ pub struct EmscriptenData<'a> {
|
||||
pub dyn_call_viijiii: Option<Func<'a, (i32, i32, i32, i32, i32, i32, i32, i32)>>,
|
||||
pub dyn_call_viijj: Option<Func<'a, (i32, i32, i32, i32, i32, i32, i32)>>,
|
||||
pub dyn_call_vj: Option<Func<'a, (i32, i32, i32)>>,
|
||||
pub dyn_call_vjji: Option<Func<'a, (i32, i32, i32, i32, i32, i32)>>,
|
||||
pub dyn_call_vij: Option<Func<'a, (i32, i32, i32, i32)>>,
|
||||
pub dyn_call_viji: Option<Func<'a, (i32, i32, i32, i32, i32)>>,
|
||||
pub dyn_call_vijiii: Option<Func<'a, (i32, i32, i32, i32, i32, i32, i32)>>,
|
||||
@ -146,11 +149,7 @@ impl<'a> EmscriptenData<'a> {
|
||||
pub fn new(instance: &'a mut Instance) -> EmscriptenData<'a> {
|
||||
let malloc = instance.func("_malloc").unwrap();
|
||||
let free = instance.func("_free").unwrap();
|
||||
let memalign = if let Ok(func) = instance.func("_memalign") {
|
||||
Some(func)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let memalign = instance.func("_memalign").ok();
|
||||
let memset = instance.func("_memset").unwrap();
|
||||
let stack_alloc = instance.func("stackAlloc").unwrap();
|
||||
|
||||
@ -198,6 +197,7 @@ impl<'a> EmscriptenData<'a> {
|
||||
let dyn_call_viijiii = instance.func("dynCall_viijiii").ok();
|
||||
let dyn_call_viijj = instance.func("dynCall_viijj").ok();
|
||||
let dyn_call_vj = instance.func("dynCall_vj").ok();
|
||||
let dyn_call_vjji = instance.func("dynCall_vjji").ok();
|
||||
let dyn_call_vij = instance.func("dynCall_vij").ok();
|
||||
let dyn_call_viji = instance.func("dynCall_viji").ok();
|
||||
let dyn_call_vijiii = instance.func("dynCall_vijiii").ok();
|
||||
@ -261,6 +261,7 @@ impl<'a> EmscriptenData<'a> {
|
||||
dyn_call_viijiii,
|
||||
dyn_call_viijj,
|
||||
dyn_call_vj,
|
||||
dyn_call_vjji,
|
||||
dyn_call_vij,
|
||||
dyn_call_viji,
|
||||
dyn_call_vijiii,
|
||||
@ -282,6 +283,7 @@ pub fn run_emscripten_instance(
|
||||
instance: &mut Instance,
|
||||
path: &str,
|
||||
args: Vec<&str>,
|
||||
entrypoint: Option<String>,
|
||||
) -> CallResult<()> {
|
||||
let mut data = EmscriptenData::new(instance);
|
||||
let data_ptr = &mut data as *mut _ as *mut c_void;
|
||||
@ -299,11 +301,19 @@ pub fn run_emscripten_instance(
|
||||
|
||||
// println!("running emscripten instance");
|
||||
|
||||
if let Some(ep) = entrypoint {
|
||||
debug!("Running entry point: {}", &ep);
|
||||
let arg = unsafe { allocate_cstr_on_stack(instance.context_mut(), args[0]).0 };
|
||||
//let (argc, argv) = store_module_arguments(instance.context_mut(), args);
|
||||
instance.call(&ep, &[Value::I32(arg as i32)])?;
|
||||
} else {
|
||||
let main_func = instance.dyn_func("_main")?;
|
||||
let num_params = main_func.signature().params().len();
|
||||
let _result = match num_params {
|
||||
2 => {
|
||||
let (argc, argv) = store_module_arguments(instance.context_mut(), path, args);
|
||||
let mut new_args = vec![path];
|
||||
new_args.extend(args);
|
||||
let (argc, argv) = store_module_arguments(instance.context_mut(), new_args);
|
||||
instance.call("_main", &[Value::I32(argc as i32), Value::I32(argv as i32)])?;
|
||||
}
|
||||
0 => {
|
||||
@ -314,37 +324,35 @@ pub fn run_emscripten_instance(
|
||||
num_params
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
// TODO atexit for emscripten
|
||||
// println!("{:?}", data);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn store_module_arguments(ctx: &mut Ctx, path: &str, args: Vec<&str>) -> (u32, u32) {
|
||||
fn store_module_arguments(ctx: &mut Ctx, args: Vec<&str>) -> (u32, u32) {
|
||||
let argc = args.len() + 1;
|
||||
|
||||
let mut args_slice = vec![0; argc];
|
||||
args_slice[0] = unsafe { allocate_cstr_on_stack(ctx, path).0 };
|
||||
for (slot, arg) in args_slice[1..argc].iter_mut().zip(args.iter()) {
|
||||
for (slot, arg) in args_slice[0..argc].iter_mut().zip(args.iter()) {
|
||||
*slot = unsafe { allocate_cstr_on_stack(ctx, &arg).0 };
|
||||
}
|
||||
|
||||
let (argv_offset, argv_slice): (_, &mut [u32]) =
|
||||
unsafe { allocate_on_stack(ctx, ((argc + 1) * 4) as u32) };
|
||||
unsafe { allocate_on_stack(ctx, ((argc) * 4) as u32) };
|
||||
assert!(!argv_slice.is_empty());
|
||||
for (slot, arg) in argv_slice[0..argc].iter_mut().zip(args_slice.iter()) {
|
||||
*slot = *arg
|
||||
}
|
||||
argv_slice[argc] = 0;
|
||||
|
||||
(argc as u32, argv_offset)
|
||||
(argc as u32 - 1, argv_offset)
|
||||
}
|
||||
|
||||
pub fn emscripten_set_up_memory(memory: &Memory, globals: &EmscriptenGlobalsData) {
|
||||
let dynamictop_ptr = globals.dynamictop_ptr;
|
||||
let stack_max = globals.stack_max;
|
||||
|
||||
let dynamic_base = align_memory(stack_max);
|
||||
let dynamic_base = globals.dynamic_base;
|
||||
|
||||
memory.view::<u32>()[(dynamictop_ptr / 4) as usize].set(dynamic_base);
|
||||
}
|
||||
@ -355,6 +363,7 @@ pub struct EmscriptenGlobalsData {
|
||||
stacktop: u32,
|
||||
stack_max: u32,
|
||||
dynamictop_ptr: u32,
|
||||
dynamic_base: u32,
|
||||
memory_base: u32,
|
||||
table_base: u32,
|
||||
temp_double_ptr: u32,
|
||||
@ -424,7 +433,14 @@ impl EmscriptenGlobals {
|
||||
let temp_double_ptr = static_top;
|
||||
static_top += 16;
|
||||
|
||||
let (dynamic_base, dynamictop_ptr) =
|
||||
get_emscripten_metadata(&module).unwrap_or_else(|| {
|
||||
let dynamictop_ptr = static_alloc(&mut static_top, 4);
|
||||
(
|
||||
align_memory(align_memory(static_top) + TOTAL_STACK),
|
||||
dynamictop_ptr,
|
||||
)
|
||||
});
|
||||
|
||||
let stacktop = align_memory(static_top);
|
||||
let stack_max = stacktop + TOTAL_STACK;
|
||||
@ -434,6 +450,7 @@ impl EmscriptenGlobals {
|
||||
stacktop,
|
||||
stack_max,
|
||||
dynamictop_ptr,
|
||||
dynamic_base,
|
||||
memory_base,
|
||||
table_base,
|
||||
temp_double_ptr,
|
||||
@ -597,6 +614,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
|
||||
"___syscall272" => func!(crate::syscalls::___syscall272),
|
||||
"___syscall295" => func!(crate::syscalls::___syscall295),
|
||||
"___syscall300" => func!(crate::syscalls::___syscall300),
|
||||
"___syscall320" => func!(crate::syscalls::___syscall320),
|
||||
"___syscall324" => func!(crate::syscalls::___syscall324),
|
||||
"___syscall330" => func!(crate::syscalls::___syscall330),
|
||||
"___syscall334" => func!(crate::syscalls::___syscall334),
|
||||
@ -718,6 +736,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
|
||||
"invoke_v" => func!(crate::emscripten_target::invoke_v),
|
||||
"invoke_vi" => func!(crate::emscripten_target::invoke_vi),
|
||||
"invoke_vj" => func!(crate::emscripten_target::invoke_vj),
|
||||
"invoke_vjji" => func!(crate::emscripten_target::invoke_vjji),
|
||||
"invoke_vii" => func!(crate::emscripten_target::invoke_vii),
|
||||
"invoke_viii" => func!(crate::emscripten_target::invoke_viii),
|
||||
"invoke_viiii" => func!(crate::emscripten_target::invoke_viiii),
|
||||
|
@ -21,8 +21,11 @@ pub fn _emscripten_memcpy_big(ctx: &mut Ctx, dest: u32, src: u32, len: u32) -> u
|
||||
|
||||
/// emscripten: _emscripten_get_heap_size
|
||||
pub fn _emscripten_get_heap_size(ctx: &mut Ctx) -> u32 {
|
||||
debug!("emscripten::_emscripten_get_heap_size",);
|
||||
ctx.memory(0).size().bytes().0 as u32
|
||||
debug!("emscripten::_emscripten_get_heap_size");
|
||||
let result = ctx.memory(0).size().bytes().0 as u32;
|
||||
debug!("=> {}", result);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
// From emscripten implementation
|
||||
|
@ -82,7 +82,7 @@ pub fn _raise(_ctx: &mut Ctx, _one: i32) -> i32 {
|
||||
}
|
||||
|
||||
pub fn _sem_init(_ctx: &mut Ctx, _one: i32, _two: i32, _three: i32) -> i32 {
|
||||
debug!("emscripten::_sem_init");
|
||||
debug!("emscripten::_sem_init: {}, {}, {}", _one, _two, _three);
|
||||
0
|
||||
}
|
||||
|
||||
|
@ -31,12 +31,13 @@ use libc::{
|
||||
off_t,
|
||||
// open,
|
||||
read,
|
||||
rename,
|
||||
// sockaddr_in,
|
||||
// readv,
|
||||
rmdir,
|
||||
// writev,
|
||||
stat,
|
||||
write,
|
||||
// sockaddr_in,
|
||||
};
|
||||
use wasmer_runtime_core::vm::Ctx;
|
||||
|
||||
@ -118,9 +119,21 @@ pub fn ___syscall20(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
|
||||
unsafe { getpid() }
|
||||
}
|
||||
|
||||
pub fn ___syscall38(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
|
||||
debug!("emscripten::___syscall38");
|
||||
-1
|
||||
// rename
|
||||
pub fn ___syscall38(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 {
|
||||
debug!("emscripten::___syscall38 (rename)");
|
||||
let old_path_addr: u32 = varargs.get(ctx);
|
||||
let new_path_addr: u32 = varargs.get(ctx);
|
||||
let old_path = emscripten_memory_pointer!(ctx.memory(0), old_path_addr) as *const i8;
|
||||
let new_path = emscripten_memory_pointer!(ctx.memory(0), new_path_addr) as *const i8;
|
||||
let result = unsafe { rename(old_path, new_path) };
|
||||
debug!(
|
||||
"=> old_path: {}, new_path: {}, result: {}",
|
||||
unsafe { std::ffi::CStr::from_ptr(old_path).to_str().unwrap() },
|
||||
unsafe { std::ffi::CStr::from_ptr(new_path).to_str().unwrap() },
|
||||
result
|
||||
);
|
||||
result
|
||||
}
|
||||
|
||||
// rmdir
|
||||
@ -246,12 +259,21 @@ pub fn ___syscall192(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
|
||||
if fd == -1 {
|
||||
let ptr = env::call_memalign(ctx, 16384, len);
|
||||
if ptr == 0 {
|
||||
return -1;
|
||||
// ENOMEM
|
||||
return -12;
|
||||
}
|
||||
let real_ptr = emscripten_memory_pointer!(ctx.memory(0), ptr) as *const u8;
|
||||
env::call_memset(ctx, ptr, 0, len);
|
||||
ptr as _
|
||||
for i in 0..(len as usize) {
|
||||
unsafe {
|
||||
assert_eq!(*real_ptr.add(i), 0);
|
||||
}
|
||||
}
|
||||
debug!("=> ptr: {}", ptr);
|
||||
return ptr as i32;
|
||||
} else {
|
||||
-1
|
||||
// return ENODEV
|
||||
return -19;
|
||||
}
|
||||
}
|
||||
|
||||
@ -461,6 +483,12 @@ pub fn ___syscall300(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
|
||||
-1
|
||||
}
|
||||
|
||||
// utimensat
|
||||
pub fn ___syscall320(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_int {
|
||||
debug!("emscripten::___syscall320 (utimensat), {}", _which);
|
||||
0
|
||||
}
|
||||
|
||||
pub fn ___syscall334(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
|
||||
debug!("emscripten::___syscall334");
|
||||
-1
|
||||
|
@ -1,4 +1,6 @@
|
||||
use crate::varargs::VarArgs;
|
||||
#[cfg(target_os = "macos")]
|
||||
use libc::size_t;
|
||||
/// NOTE: TODO: These syscalls only support wasm_32 for now because they assume offsets are u32
|
||||
/// Syscall list: https://www.cs.utexas.edu/~bismith/test/syscalls/syscalls32.html
|
||||
use libc::{
|
||||
@ -53,7 +55,6 @@ use libc::{
|
||||
sendto,
|
||||
setpgid,
|
||||
setsockopt,
|
||||
size_t,
|
||||
sockaddr,
|
||||
socket,
|
||||
socklen_t,
|
||||
@ -251,8 +252,9 @@ pub fn ___syscall33(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int
|
||||
let path = emscripten_memory_pointer!(ctx.memory(0), path_ptr) as *const i8;
|
||||
let result = unsafe { access(path, amode) };
|
||||
debug!(
|
||||
"=> path: {}, result: {}",
|
||||
"=> path: {}, amode: {}, result: {}",
|
||||
unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() },
|
||||
amode,
|
||||
result
|
||||
);
|
||||
result
|
||||
@ -354,8 +356,13 @@ pub fn ___syscall54(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int
|
||||
debug!("emscripten::___syscall54 (ioctl) {}", _which);
|
||||
let fd: i32 = varargs.get(ctx);
|
||||
let request: u32 = varargs.get(ctx);
|
||||
debug!("fd: {}, op: {}", fd, request);
|
||||
debug!("=> fd: {}, op: {}", fd, request);
|
||||
// Got the equivalents here: https://code.woboq.org/linux/linux/include/uapi/asm-generic/ioctls.h.html
|
||||
// let argp: u32 = varargs.get(ctx);
|
||||
// let argp_ptr = emscripten_memory_pointer!(ctx.memory(0), argp) as *mut c_void;
|
||||
// let ret = unsafe { ioctl(fd, request as _, argp_ptr) };
|
||||
// debug!("=> {}", ret);
|
||||
// ret
|
||||
match request as _ {
|
||||
21537 => {
|
||||
// FIONBIO
|
||||
|
@ -28,7 +28,9 @@ use wasmer_runtime_core::vm::Ctx;
|
||||
use libc::{CLOCK_MONOTONIC, CLOCK_MONOTONIC_COARSE, CLOCK_REALTIME};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use libc::{CLOCK_MONOTONIC, CLOCK_REALTIME};
|
||||
use libc::CLOCK_REALTIME;
|
||||
#[cfg(target_os = "macos")]
|
||||
const CLOCK_MONOTONIC: clockid_t = 1;
|
||||
#[cfg(target_os = "macos")]
|
||||
const CLOCK_MONOTONIC_COARSE: clockid_t = 6;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
use super::env;
|
||||
use super::env::get_emscripten_data;
|
||||
use crate::storage::align_memory;
|
||||
use libc::stat;
|
||||
use std::ffi::CStr;
|
||||
use std::mem::size_of;
|
||||
@ -39,6 +40,43 @@ pub fn get_emscripten_memory_size(module: &Module) -> (Pages, Option<Pages>) {
|
||||
(memory.minimum, memory.maximum)
|
||||
}
|
||||
|
||||
/// Reads values written by `-s EMIT_EMSCRIPTEN_METADATA=1`
|
||||
/// Assumes values start from the end in this order:
|
||||
/// Last export: Dynamic Base
|
||||
/// Second-to-Last export: Dynamic top pointer
|
||||
pub fn get_emscripten_metadata(module: &Module) -> Option<(u32, u32)> {
|
||||
let max_idx = &module.info().globals.iter().map(|(k, _)| k).max()?;
|
||||
let snd_max_idx = &module
|
||||
.info()
|
||||
.globals
|
||||
.iter()
|
||||
.map(|(k, _)| k)
|
||||
.filter(|k| k != max_idx)
|
||||
.max()?;
|
||||
|
||||
use wasmer_runtime_core::types::{GlobalInit, Initializer::Const, Value::I32};
|
||||
if let (
|
||||
GlobalInit {
|
||||
init: Const(I32(dynamic_base)),
|
||||
..
|
||||
},
|
||||
GlobalInit {
|
||||
init: Const(I32(dynamictop_ptr)),
|
||||
..
|
||||
},
|
||||
) = (
|
||||
&module.info().globals[*max_idx],
|
||||
&module.info().globals[*snd_max_idx],
|
||||
) {
|
||||
Some((
|
||||
align_memory(*dynamic_base as u32 - 32),
|
||||
align_memory(*dynamictop_ptr as u32 - 32),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn write_to_buf(ctx: &mut Ctx, string: *const c_char, buf: u32, max: u32) -> u32 {
|
||||
let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as *mut c_char;
|
||||
|
||||
|
@ -53,6 +53,7 @@ macro_rules! assert_emscripten_output {
|
||||
&mut instance,
|
||||
$name,
|
||||
$args,
|
||||
None,
|
||||
).expect("run_emscripten_instance finishes");
|
||||
|
||||
let output = capturer.end().unwrap().0;
|
||||
|
@ -1,11 +1,11 @@
|
||||
[package]
|
||||
name = "wasmer-llvm-backend"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
authors = ["Lachlan Sneff <lachlan.sneff@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.3.0" }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" }
|
||||
inkwell = { git = "https://github.com/wasmerio/inkwell", branch = "llvm7-0" }
|
||||
wasmparser = "0.29.2"
|
||||
hashbrown = "0.1.8"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -369,31 +369,6 @@ impl Intrinsics {
|
||||
ctx_ptr_ty,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ctx<'a>(
|
||||
&'a self,
|
||||
info: &'a ModuleInfo,
|
||||
builder: &'a Builder,
|
||||
func_value: &'a FunctionValue,
|
||||
cache_builder: Builder,
|
||||
) -> CtxType<'a> {
|
||||
CtxType {
|
||||
ctx_ptr_value: func_value.get_nth_param(0).unwrap().into_pointer_value(),
|
||||
|
||||
builder,
|
||||
intrinsics: self,
|
||||
info,
|
||||
cache_builder,
|
||||
|
||||
cached_memories: HashMap::new(),
|
||||
cached_tables: HashMap::new(),
|
||||
cached_sigindices: HashMap::new(),
|
||||
cached_globals: HashMap::new(),
|
||||
cached_imported_functions: HashMap::new(),
|
||||
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@ -429,8 +404,6 @@ struct ImportedFuncCache {
|
||||
pub struct CtxType<'a> {
|
||||
ctx_ptr_value: PointerValue,
|
||||
|
||||
builder: &'a Builder,
|
||||
intrinsics: &'a Intrinsics,
|
||||
info: &'a ModuleInfo,
|
||||
cache_builder: Builder,
|
||||
|
||||
@ -444,16 +417,36 @@ pub struct CtxType<'a> {
|
||||
}
|
||||
|
||||
impl<'a> CtxType<'a> {
|
||||
pub fn new(
|
||||
info: &'a ModuleInfo,
|
||||
func_value: &'a FunctionValue,
|
||||
cache_builder: Builder,
|
||||
) -> CtxType<'a> {
|
||||
CtxType {
|
||||
ctx_ptr_value: func_value.get_nth_param(0).unwrap().into_pointer_value(),
|
||||
|
||||
info,
|
||||
cache_builder,
|
||||
|
||||
cached_memories: HashMap::new(),
|
||||
cached_tables: HashMap::new(),
|
||||
cached_sigindices: HashMap::new(),
|
||||
cached_globals: HashMap::new(),
|
||||
cached_imported_functions: HashMap::new(),
|
||||
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn basic(&self) -> BasicValueEnum {
|
||||
self.ctx_ptr_value.as_basic_value_enum()
|
||||
}
|
||||
|
||||
pub fn memory(&mut self, index: MemoryIndex) -> MemoryCache {
|
||||
let (cached_memories, info, ctx_ptr_value, intrinsics, cache_builder) = (
|
||||
pub fn memory(&mut self, index: MemoryIndex, intrinsics: &Intrinsics) -> MemoryCache {
|
||||
let (cached_memories, info, ctx_ptr_value, cache_builder) = (
|
||||
&mut self.cached_memories,
|
||||
self.info,
|
||||
self.ctx_ptr_value,
|
||||
self.intrinsics,
|
||||
&self.cache_builder,
|
||||
);
|
||||
|
||||
@ -514,13 +507,16 @@ impl<'a> CtxType<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn table(&mut self, index: TableIndex) -> (PointerValue, IntValue) {
|
||||
let (cached_tables, builder, info, ctx_ptr_value, intrinsics, cache_builder) = (
|
||||
pub fn table(
|
||||
&mut self,
|
||||
index: TableIndex,
|
||||
intrinsics: &Intrinsics,
|
||||
builder: &Builder,
|
||||
) -> (PointerValue, IntValue) {
|
||||
let (cached_tables, info, ctx_ptr_value, cache_builder) = (
|
||||
&mut self.cached_tables,
|
||||
self.builder,
|
||||
self.info,
|
||||
self.ctx_ptr_value,
|
||||
self.intrinsics,
|
||||
&self.cache_builder,
|
||||
);
|
||||
|
||||
@ -575,41 +571,39 @@ impl<'a> CtxType<'a> {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn local_func(&mut self, index: LocalFuncIndex, fn_ty: FunctionType) -> PointerValue {
|
||||
let local_func_array_ptr_ptr = unsafe {
|
||||
self.builder
|
||||
.build_struct_gep(self.ctx_ptr_value, 8, "local_func_array_ptr_ptr")
|
||||
};
|
||||
let local_func_array_ptr = self
|
||||
.builder
|
||||
pub fn local_func(
|
||||
&mut self,
|
||||
index: LocalFuncIndex,
|
||||
fn_ty: FunctionType,
|
||||
intrinsics: &Intrinsics,
|
||||
builder: &Builder,
|
||||
) -> PointerValue {
|
||||
let local_func_array_ptr_ptr =
|
||||
unsafe { builder.build_struct_gep(self.ctx_ptr_value, 8, "local_func_array_ptr_ptr") };
|
||||
let local_func_array_ptr = builder
|
||||
.build_load(local_func_array_ptr_ptr, "local_func_array_ptr")
|
||||
.into_pointer_value();
|
||||
let local_func_ptr_ptr = unsafe {
|
||||
self.builder.build_in_bounds_gep(
|
||||
builder.build_in_bounds_gep(
|
||||
local_func_array_ptr,
|
||||
&[self
|
||||
.intrinsics
|
||||
.i32_ty
|
||||
.const_int(index.index() as u64, false)],
|
||||
&[intrinsics.i32_ty.const_int(index.index() as u64, false)],
|
||||
"local_func_ptr_ptr",
|
||||
)
|
||||
};
|
||||
let local_func_ptr = self
|
||||
.builder
|
||||
let local_func_ptr = builder
|
||||
.build_load(local_func_ptr_ptr, "local_func_ptr")
|
||||
.into_pointer_value();
|
||||
self.builder.build_pointer_cast(
|
||||
builder.build_pointer_cast(
|
||||
local_func_ptr,
|
||||
fn_ty.ptr_type(AddressSpace::Generic),
|
||||
"local_func_ptr",
|
||||
)
|
||||
}
|
||||
|
||||
pub fn dynamic_sigindex(&mut self, index: SigIndex) -> IntValue {
|
||||
let (cached_sigindices, ctx_ptr_value, intrinsics, cache_builder) = (
|
||||
pub fn dynamic_sigindex(&mut self, index: SigIndex, intrinsics: &Intrinsics) -> IntValue {
|
||||
let (cached_sigindices, ctx_ptr_value, cache_builder) = (
|
||||
&mut self.cached_sigindices,
|
||||
self.ctx_ptr_value,
|
||||
self.intrinsics,
|
||||
&self.cache_builder,
|
||||
);
|
||||
|
||||
@ -636,12 +630,11 @@ impl<'a> CtxType<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn global_cache(&mut self, index: GlobalIndex) -> GlobalCache {
|
||||
let (cached_globals, ctx_ptr_value, info, intrinsics, cache_builder) = (
|
||||
pub fn global_cache(&mut self, index: GlobalIndex, intrinsics: &Intrinsics) -> GlobalCache {
|
||||
let (cached_globals, ctx_ptr_value, info, cache_builder) = (
|
||||
&mut self.cached_globals,
|
||||
self.ctx_ptr_value,
|
||||
self.info,
|
||||
self.intrinsics,
|
||||
&self.cache_builder,
|
||||
);
|
||||
|
||||
@ -712,11 +705,14 @@ impl<'a> CtxType<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn imported_func(&mut self, index: ImportedFuncIndex) -> (PointerValue, PointerValue) {
|
||||
let (cached_imported_functions, ctx_ptr_value, intrinsics, cache_builder) = (
|
||||
pub fn imported_func(
|
||||
&mut self,
|
||||
index: ImportedFuncIndex,
|
||||
intrinsics: &Intrinsics,
|
||||
) -> (PointerValue, PointerValue) {
|
||||
let (cached_imported_functions, ctx_ptr_value, cache_builder) = (
|
||||
&mut self.cached_imported_functions,
|
||||
self.ctx_ptr_value,
|
||||
self.intrinsics,
|
||||
&self.cache_builder,
|
||||
);
|
||||
|
||||
|
@ -1,13 +1,6 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
#![cfg_attr(nightly, feature(unwind_attributes))]
|
||||
|
||||
use wasmer_runtime_core::{
|
||||
backend::{Compiler, CompilerConfig, Token},
|
||||
cache::{Artifact, Error as CacheError},
|
||||
error::CompileError,
|
||||
module::ModuleInner,
|
||||
};
|
||||
use wasmparser::{self, WasmDecoder};
|
||||
|
||||
mod backend;
|
||||
mod code;
|
||||
mod intrinsics;
|
||||
@ -16,111 +9,11 @@ mod read_info;
|
||||
mod state;
|
||||
mod trampolines;
|
||||
|
||||
pub struct LLVMCompiler {
|
||||
_private: (),
|
||||
}
|
||||
use wasmer_runtime_core::codegen::SimpleStreamingCompilerGen;
|
||||
|
||||
impl LLVMCompiler {
|
||||
pub fn new() -> Self {
|
||||
Self { _private: () }
|
||||
}
|
||||
}
|
||||
|
||||
impl Compiler for LLVMCompiler {
|
||||
fn compile(
|
||||
&self,
|
||||
wasm: &[u8],
|
||||
compiler_config: CompilerConfig,
|
||||
_: Token,
|
||||
) -> Result<ModuleInner, CompileError> {
|
||||
validate(wasm)?;
|
||||
|
||||
let (info, code_reader) = read_info::read_module(wasm, compiler_config).unwrap();
|
||||
let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap();
|
||||
|
||||
let (backend, cache_gen) = backend::LLVMBackend::new(module, intrinsics);
|
||||
|
||||
Ok(ModuleInner {
|
||||
runnable_module: Box::new(backend),
|
||||
cache_gen: Box::new(cache_gen),
|
||||
|
||||
info,
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn from_cache(&self, artifact: Artifact, _: Token) -> Result<ModuleInner, CacheError> {
|
||||
let (info, _, memory) = artifact.consume();
|
||||
let (backend, cache_gen) =
|
||||
backend::LLVMBackend::from_buffer(memory).map_err(CacheError::DeserializeError)?;
|
||||
|
||||
Ok(ModuleInner {
|
||||
runnable_module: Box::new(backend),
|
||||
cache_gen: Box::new(cache_gen),
|
||||
|
||||
info,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn validate(bytes: &[u8]) -> Result<(), CompileError> {
|
||||
let mut parser = wasmparser::ValidatingParser::new(
|
||||
bytes,
|
||||
Some(wasmparser::ValidatingParserConfig {
|
||||
operator_config: wasmparser::OperatorValidatorConfig {
|
||||
enable_threads: false,
|
||||
enable_reference_types: false,
|
||||
enable_simd: false,
|
||||
enable_bulk_memory: false,
|
||||
},
|
||||
mutable_global_imports: false,
|
||||
}),
|
||||
);
|
||||
|
||||
loop {
|
||||
let state = parser.read();
|
||||
match *state {
|
||||
wasmparser::ParserState::EndWasm => break Ok(()),
|
||||
wasmparser::ParserState::Error(err) => Err(CompileError::ValidationError {
|
||||
msg: err.message.to_string(),
|
||||
})?,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_module() {
|
||||
use std::mem::transmute;
|
||||
use wabt::wat2wasm;
|
||||
use wasmer_runtime_core::{
|
||||
backend::RunnableModule, structures::TypedIndex, types::LocalFuncIndex, vm,
|
||||
};
|
||||
// let wasm = include_bytes!("../../spectests/examples/simple/simple.wasm") as &[u8];
|
||||
let wat = r#"
|
||||
(module
|
||||
(type $t0 (func (param i32) (result i32)))
|
||||
(type $t1 (func (result i32)))
|
||||
(memory 1)
|
||||
(global $g0 (mut i32) (i32.const 0))
|
||||
(func $foo (type $t0) (param i32) (result i32)
|
||||
get_local 0
|
||||
))
|
||||
"#;
|
||||
let wasm = wat2wasm(wat).unwrap();
|
||||
|
||||
let (info, code_reader) = read_info::read_module(&wasm, Default::default()).unwrap();
|
||||
|
||||
let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap();
|
||||
|
||||
let (backend, _) = backend::LLVMBackend::new(module, intrinsics);
|
||||
|
||||
let func_ptr = backend.get_func(&info, LocalFuncIndex::new(0)).unwrap();
|
||||
|
||||
println!("func_ptr: {:p}", func_ptr.as_ptr());
|
||||
|
||||
unsafe {
|
||||
let func: unsafe extern "C" fn(*mut vm::Ctx, i32) -> i32 = transmute(func_ptr);
|
||||
let result = func(0 as _, 42);
|
||||
println!("result: {}", result);
|
||||
}
|
||||
}
|
||||
pub type LLVMCompiler = SimpleStreamingCompilerGen<
|
||||
code::LLVMModuleCodeGenerator,
|
||||
code::LLVMFunctionCodeGenerator,
|
||||
backend::LLVMBackend,
|
||||
code::CodegenError,
|
||||
>;
|
||||
|
@ -34,7 +34,7 @@ pub unsafe fn visit_fde(addr: *mut u8, size: usize, visitor: extern "C" fn(*mut
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
pub unsafe fn visit_fde(addr: *mut u8, size: usize, visitor: extern "C" fn(*mut u8)) {
|
||||
pub unsafe fn visit_fde(addr: *mut u8, _size: usize, visitor: extern "C" fn(*mut u8)) {
|
||||
visitor(addr);
|
||||
}
|
||||
|
||||
|
@ -1,289 +1,5 @@
|
||||
use wasmer_runtime_core::{
|
||||
backend::{Backend, CompilerConfig},
|
||||
module::{
|
||||
DataInitializer, ExportIndex, ImportName, ModuleInfo, StringTable, StringTableBuilder,
|
||||
TableInitializer,
|
||||
},
|
||||
structures::{Map, TypedIndex},
|
||||
types::{
|
||||
ElementType, FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit,
|
||||
ImportedGlobalIndex, Initializer, MemoryDescriptor, MemoryIndex, SigIndex, TableDescriptor,
|
||||
TableIndex, Type, Value,
|
||||
},
|
||||
units::Pages,
|
||||
};
|
||||
use wasmparser::{
|
||||
BinaryReaderError, CodeSectionReader, Data, DataKind, Element, ElementKind, Export,
|
||||
ExternalKind, FuncType, Import, ImportSectionEntryType, InitExpr, ModuleReader, Operator,
|
||||
SectionCode, Type as WpType,
|
||||
};
|
||||
|
||||
use hashbrown::HashMap;
|
||||
|
||||
pub fn read_module(
|
||||
wasm: &[u8],
|
||||
compiler_config: CompilerConfig,
|
||||
) -> Result<(ModuleInfo, CodeSectionReader), BinaryReaderError> {
|
||||
let mut info = ModuleInfo {
|
||||
memories: Map::new(),
|
||||
globals: Map::new(),
|
||||
tables: Map::new(),
|
||||
|
||||
imported_functions: Map::new(),
|
||||
imported_memories: Map::new(),
|
||||
imported_tables: Map::new(),
|
||||
imported_globals: Map::new(),
|
||||
|
||||
exports: Default::default(),
|
||||
|
||||
data_initializers: Vec::new(),
|
||||
elem_initializers: Vec::new(),
|
||||
|
||||
start_func: None,
|
||||
|
||||
func_assoc: Map::new(),
|
||||
signatures: Map::new(),
|
||||
backend: Backend::LLVM,
|
||||
|
||||
namespace_table: StringTable::new(),
|
||||
name_table: StringTable::new(),
|
||||
|
||||
em_symbol_map: compiler_config.symbol_map.clone(),
|
||||
|
||||
custom_sections: HashMap::new(),
|
||||
};
|
||||
|
||||
let mut reader = ModuleReader::new(wasm)?;
|
||||
let mut code_reader = None;
|
||||
|
||||
loop {
|
||||
if reader.eof() {
|
||||
return Ok((info, code_reader.unwrap()));
|
||||
}
|
||||
|
||||
let section = reader.read()?;
|
||||
|
||||
match section.code {
|
||||
SectionCode::Type => {
|
||||
let type_reader = section.get_type_section_reader()?;
|
||||
|
||||
for ty in type_reader {
|
||||
let ty = ty?;
|
||||
info.signatures.push(func_type_to_func_sig(ty)?);
|
||||
}
|
||||
}
|
||||
SectionCode::Import => {
|
||||
let import_reader = section.get_import_section_reader()?;
|
||||
let mut namespace_builder = StringTableBuilder::new();
|
||||
let mut name_builder = StringTableBuilder::new();
|
||||
|
||||
for import in import_reader {
|
||||
let Import { module, field, ty } = import?;
|
||||
|
||||
let namespace_index = namespace_builder.register(module);
|
||||
let name_index = name_builder.register(field);
|
||||
let import_name = ImportName {
|
||||
namespace_index,
|
||||
name_index,
|
||||
};
|
||||
|
||||
match ty {
|
||||
ImportSectionEntryType::Function(sigindex) => {
|
||||
let sigindex = SigIndex::new(sigindex as usize);
|
||||
info.imported_functions.push(import_name);
|
||||
info.func_assoc.push(sigindex);
|
||||
}
|
||||
ImportSectionEntryType::Table(table_ty) => {
|
||||
assert_eq!(table_ty.element_type, WpType::AnyFunc);
|
||||
let table_desc = TableDescriptor {
|
||||
element: ElementType::Anyfunc,
|
||||
minimum: table_ty.limits.initial,
|
||||
maximum: table_ty.limits.maximum,
|
||||
};
|
||||
|
||||
info.imported_tables.push((import_name, table_desc));
|
||||
}
|
||||
ImportSectionEntryType::Memory(memory_ty) => {
|
||||
let mem_desc = MemoryDescriptor {
|
||||
minimum: Pages(memory_ty.limits.initial),
|
||||
maximum: memory_ty.limits.maximum.map(|max| Pages(max)),
|
||||
shared: memory_ty.shared,
|
||||
};
|
||||
info.imported_memories.push((import_name, mem_desc));
|
||||
}
|
||||
ImportSectionEntryType::Global(global_ty) => {
|
||||
let global_desc = GlobalDescriptor {
|
||||
mutable: global_ty.mutable,
|
||||
ty: type_to_type(global_ty.content_type)?,
|
||||
};
|
||||
info.imported_globals.push((import_name, global_desc));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info.namespace_table = namespace_builder.finish();
|
||||
info.name_table = name_builder.finish();
|
||||
}
|
||||
SectionCode::Function => {
|
||||
let func_decl_reader = section.get_function_section_reader()?;
|
||||
|
||||
for sigindex in func_decl_reader {
|
||||
let sigindex = sigindex?;
|
||||
|
||||
let sigindex = SigIndex::new(sigindex as usize);
|
||||
info.func_assoc.push(sigindex);
|
||||
}
|
||||
}
|
||||
SectionCode::Table => {
|
||||
let table_decl_reader = section.get_table_section_reader()?;
|
||||
|
||||
for table_ty in table_decl_reader {
|
||||
let table_ty = table_ty?;
|
||||
|
||||
let table_desc = TableDescriptor {
|
||||
element: ElementType::Anyfunc,
|
||||
minimum: table_ty.limits.initial,
|
||||
maximum: table_ty.limits.maximum,
|
||||
};
|
||||
|
||||
info.tables.push(table_desc);
|
||||
}
|
||||
}
|
||||
SectionCode::Memory => {
|
||||
let mem_decl_reader = section.get_memory_section_reader()?;
|
||||
|
||||
for memory_ty in mem_decl_reader {
|
||||
let memory_ty = memory_ty?;
|
||||
|
||||
let mem_desc = MemoryDescriptor {
|
||||
minimum: Pages(memory_ty.limits.initial),
|
||||
maximum: memory_ty.limits.maximum.map(|max| Pages(max)),
|
||||
shared: memory_ty.shared,
|
||||
};
|
||||
|
||||
info.memories.push(mem_desc);
|
||||
}
|
||||
}
|
||||
SectionCode::Global => {
|
||||
let global_decl_reader = section.get_global_section_reader()?;
|
||||
|
||||
for global in global_decl_reader {
|
||||
let global = global?;
|
||||
|
||||
let desc = GlobalDescriptor {
|
||||
mutable: global.ty.mutable,
|
||||
ty: type_to_type(global.ty.content_type)?,
|
||||
};
|
||||
|
||||
let global_init = GlobalInit {
|
||||
desc,
|
||||
init: eval_init_expr(&global.init_expr)?,
|
||||
};
|
||||
|
||||
info.globals.push(global_init);
|
||||
}
|
||||
}
|
||||
SectionCode::Export => {
|
||||
let export_reader = section.get_export_section_reader()?;
|
||||
|
||||
for export in export_reader {
|
||||
let Export { field, kind, index } = export?;
|
||||
|
||||
let export_index = match kind {
|
||||
ExternalKind::Function => ExportIndex::Func(FuncIndex::new(index as usize)),
|
||||
ExternalKind::Table => ExportIndex::Table(TableIndex::new(index as usize)),
|
||||
ExternalKind::Memory => {
|
||||
ExportIndex::Memory(MemoryIndex::new(index as usize))
|
||||
}
|
||||
ExternalKind::Global => {
|
||||
ExportIndex::Global(GlobalIndex::new(index as usize))
|
||||
}
|
||||
};
|
||||
|
||||
info.exports.insert(field.to_string(), export_index);
|
||||
}
|
||||
}
|
||||
SectionCode::Start => {
|
||||
let start_index = section.get_start_section_content()?;
|
||||
|
||||
info.start_func = Some(FuncIndex::new(start_index as usize));
|
||||
}
|
||||
SectionCode::Element => {
|
||||
let element_reader = section.get_element_section_reader()?;
|
||||
|
||||
for element in element_reader {
|
||||
let Element { kind, items } = element?;
|
||||
|
||||
match kind {
|
||||
ElementKind::Active {
|
||||
table_index,
|
||||
init_expr,
|
||||
} => {
|
||||
let table_index = TableIndex::new(table_index as usize);
|
||||
let base = eval_init_expr(&init_expr)?;
|
||||
let items_reader = items.get_items_reader()?;
|
||||
|
||||
let elements: Vec<_> = items_reader
|
||||
.into_iter()
|
||||
.map(|res| res.map(|index| FuncIndex::new(index as usize)))
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
let table_init = TableInitializer {
|
||||
table_index,
|
||||
base,
|
||||
elements,
|
||||
};
|
||||
|
||||
info.elem_initializers.push(table_init);
|
||||
}
|
||||
ElementKind::Passive(_ty) => {
|
||||
return Err(BinaryReaderError {
|
||||
message: "passive tables are not yet supported",
|
||||
offset: -1isize as usize,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SectionCode::Code => {
|
||||
code_reader = Some(section.get_code_section_reader()?);
|
||||
}
|
||||
SectionCode::Data => {
|
||||
let data_reader = section.get_data_section_reader()?;
|
||||
|
||||
for data in data_reader {
|
||||
let Data { kind, data } = data?;
|
||||
|
||||
match kind {
|
||||
DataKind::Active {
|
||||
memory_index,
|
||||
init_expr,
|
||||
} => {
|
||||
let memory_index = MemoryIndex::new(memory_index as usize);
|
||||
let base = eval_init_expr(&init_expr)?;
|
||||
|
||||
let data_init = DataInitializer {
|
||||
memory_index,
|
||||
base,
|
||||
data: data.to_vec(),
|
||||
};
|
||||
|
||||
info.data_initializers.push(data_init);
|
||||
}
|
||||
DataKind::Passive => {
|
||||
return Err(BinaryReaderError {
|
||||
message: "passive memories are not yet supported",
|
||||
offset: -1isize as usize,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SectionCode::DataCount => {}
|
||||
SectionCode::Custom { .. } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
use wasmer_runtime_core::types::Type;
|
||||
use wasmparser::{BinaryReaderError, Type as WpType};
|
||||
|
||||
pub fn type_to_type(ty: WpType) -> Result<Type, BinaryReaderError> {
|
||||
Ok(match ty {
|
||||
@ -305,46 +21,3 @@ pub fn type_to_type(ty: WpType) -> Result<Type, BinaryReaderError> {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn func_type_to_func_sig(func_ty: FuncType) -> Result<FuncSig, BinaryReaderError> {
|
||||
assert_eq!(func_ty.form, WpType::Func);
|
||||
|
||||
Ok(FuncSig::new(
|
||||
func_ty
|
||||
.params
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(type_to_type)
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
func_ty
|
||||
.returns
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(type_to_type)
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
))
|
||||
}
|
||||
|
||||
fn eval_init_expr(expr: &InitExpr) -> Result<Initializer, BinaryReaderError> {
|
||||
let mut reader = expr.get_operators_reader();
|
||||
let (op, offset) = reader.read_with_offset()?;
|
||||
Ok(match op {
|
||||
Operator::GetGlobal { global_index } => {
|
||||
Initializer::GetGlobal(ImportedGlobalIndex::new(global_index as usize))
|
||||
}
|
||||
Operator::I32Const { value } => Initializer::Const(Value::I32(value)),
|
||||
Operator::I64Const { value } => Initializer::Const(Value::I64(value)),
|
||||
Operator::F32Const { value } => {
|
||||
Initializer::Const(Value::F32(f32::from_bits(value.bits())))
|
||||
}
|
||||
Operator::F64Const { value } => {
|
||||
Initializer::Const(Value::F64(f64::from_bits(value.bits())))
|
||||
}
|
||||
_ => {
|
||||
return Err(BinaryReaderError {
|
||||
message: "init expr evaluation failed: unsupported opcode",
|
||||
offset,
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-middleware-common"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
repository = "https://github.com/wasmerio/wasmer"
|
||||
description = "Wasmer runtime common middlewares"
|
||||
license = "MIT"
|
||||
@ -8,4 +8,4 @@ authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.3.0" }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" }
|
||||
|
@ -10,7 +10,7 @@ impl FunctionMiddleware for CallTrace {
|
||||
fn feed_event<'a, 'b: 'a>(
|
||||
&mut self,
|
||||
op: Event<'a, 'b>,
|
||||
module_info: &ModuleInfo,
|
||||
_module_info: &ModuleInfo,
|
||||
sink: &mut EventSink<'a, 'b>,
|
||||
) -> Result<(), Self::Error> {
|
||||
match op {
|
||||
|
@ -1 +1,3 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
pub mod call_trace;
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-runtime-abi"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
description = "Wasmer runtime core library"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
|
@ -1,3 +1,5 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[macro_use]
|
||||
extern crate failure;
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-runtime-c-api"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
description = "Wasmer C API library"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
@ -16,11 +16,11 @@ libc = "0.2"
|
||||
|
||||
[dependencies.wasmer-runtime]
|
||||
path = "../runtime"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
|
||||
[dependencies.wasmer-runtime-core]
|
||||
path = "../runtime-core"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
|
||||
[features]
|
||||
debug = ["wasmer-runtime/debug"]
|
||||
|
@ -1,4 +1,4 @@
|
||||
//! Errors.
|
||||
//! Read runtime errors.
|
||||
|
||||
use libc::{c_char, c_int};
|
||||
use std::cell::RefCell;
|
||||
@ -61,20 +61,20 @@ pub unsafe extern "C" fn wasmer_last_error_message(buffer: *mut c_char, length:
|
||||
return -1;
|
||||
}
|
||||
|
||||
let last_error = match take_last_error() {
|
||||
Some(err) => err,
|
||||
let error_message = match take_last_error() {
|
||||
Some(err) => err.to_string(),
|
||||
None => return 0,
|
||||
};
|
||||
|
||||
let error_message = last_error.to_string();
|
||||
let length = length as usize;
|
||||
|
||||
let buffer = slice::from_raw_parts_mut(buffer as *mut u8, length as usize);
|
||||
|
||||
if error_message.len() >= buffer.len() {
|
||||
// buffer to small for err message
|
||||
if error_message.len() >= length {
|
||||
// buffer is too small to hold the error message
|
||||
return -1;
|
||||
}
|
||||
|
||||
let buffer = slice::from_raw_parts_mut(buffer as *mut u8, length);
|
||||
|
||||
ptr::copy_nonoverlapping(
|
||||
error_message.as_ptr(),
|
||||
buffer.as_mut_ptr(),
|
||||
@ -85,7 +85,7 @@ pub unsafe extern "C" fn wasmer_last_error_message(buffer: *mut c_char, length:
|
||||
// accidentally read into garbage.
|
||||
buffer[error_message.len()] = 0;
|
||||
|
||||
error_message.len() as c_int
|
||||
error_message.len() as c_int + 1
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -1,4 +1,5 @@
|
||||
//! Wasm exports.
|
||||
//! Create, read, destroy export definitions (function, global, memory
|
||||
//! and table) on an instance.
|
||||
|
||||
use crate::{
|
||||
error::{update_last_error, CApiError},
|
||||
|
@ -1,4 +1,4 @@
|
||||
//! Wasm global.
|
||||
//! Create, set, get and destroy global variables of an instance.
|
||||
|
||||
use crate::value::{wasmer_value_t, wasmer_value_tag};
|
||||
use wasmer_runtime::Global;
|
||||
|
@ -1,4 +1,5 @@
|
||||
//! Wasm imports.
|
||||
//! Create, read, destroy import definitions (function, global, memory
|
||||
//! and table) on an instance.
|
||||
|
||||
use crate::{
|
||||
error::{update_last_error, CApiError},
|
||||
|
@ -1,4 +1,4 @@
|
||||
//! Wasm instance.
|
||||
//! Instantiate a module, call functions, and read exports.
|
||||
|
||||
use crate::{
|
||||
error::{update_last_error, CApiError},
|
||||
|
@ -1,3 +1,87 @@
|
||||
//! # Wasmer Runtime C API
|
||||
//!
|
||||
//! Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully
|
||||
//! compatible with Emscripten, Rust and Go. [Learn
|
||||
//! more](https://github.com/wasmerio/wasmer).
|
||||
//!
|
||||
//! This crate exposes a C and C++ API for the Wasmer runtime.
|
||||
//!
|
||||
//! # Usage
|
||||
//!
|
||||
//! The C and C++ header files can be found in the source tree of this
|
||||
//! crate, respectively [`wasmer.h`][wasmer_h] and
|
||||
//! [`wasmer.hh`][wasmer_hh]. They are automatically generated, and always
|
||||
//! up-to-date in this repository.
|
||||
//!
|
||||
//! Here is a simple example to use the C API:
|
||||
//!
|
||||
//! ```c
|
||||
//! #include <stdio.h>
|
||||
//! #include "wasmer.h"
|
||||
//! #include <assert.h>
|
||||
//! #include <stdint.h>
|
||||
//!
|
||||
//! int main()
|
||||
//! {
|
||||
//! // Read the Wasm file bytes.
|
||||
//! FILE *file = fopen("sum.wasm", "r");
|
||||
//! fseek(file, 0, SEEK_END);
|
||||
//! long len = ftell(file);
|
||||
//! uint8_t *bytes = malloc(len);
|
||||
//! fseek(file, 0, SEEK_SET);
|
||||
//! fread(bytes, 1, len, file);
|
||||
//! fclose(file);
|
||||
//!
|
||||
//! // Prepare the imports.
|
||||
//! wasmer_import_t imports[] = {};
|
||||
//!
|
||||
//! // Instantiate!
|
||||
//! wasmer_instance_t *instance = NULL;
|
||||
//! wasmer_result_t instantiation_result = wasmer_instantiate(&instance, bytes, len, imports, 0);
|
||||
//!
|
||||
//! assert(instantiation_result == WASMER_OK);
|
||||
//!
|
||||
//! // Let's call a function.
|
||||
//! // Start by preparing the arguments.
|
||||
//!
|
||||
//! // Value of argument #1 is `7i32`.
|
||||
//! wasmer_value_t argument_one;
|
||||
//! argument_one.tag = WASM_I32;
|
||||
//! argument_one.value.I32 = 7;
|
||||
//!
|
||||
//! // Value of argument #2 is `8i32`.
|
||||
//! wasmer_value_t argument_two;
|
||||
//! argument_two.tag = WASM_I32;
|
||||
//! argument_two.value.I32 = 8;
|
||||
//!
|
||||
//! // Prepare the arguments.
|
||||
//! wasmer_value_t arguments[] = {argument_one, argument_two};
|
||||
//!
|
||||
//! // Prepare the return value.
|
||||
//! wasmer_value_t result_one;
|
||||
//! wasmer_value_t results[] = {result_one};
|
||||
//!
|
||||
//! // Call the `sum` function with the prepared arguments and the return value.
|
||||
//! wasmer_result_t call_result = wasmer_instance_call(instance, "sum", arguments, 2, results, 1);
|
||||
//!
|
||||
//! // Let's display the result.
|
||||
//! printf("Call result: %d\n", call_result);
|
||||
//! printf("Result: %d\n", results[0].value.I32);
|
||||
//!
|
||||
//! // `sum(7, 8) == 15`.
|
||||
//! assert(results[0].value.I32 == 15);
|
||||
//! assert(call_result == WASMER_OK);
|
||||
//!
|
||||
//! wasmer_instance_destroy(instance);
|
||||
//!
|
||||
//! return 0;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! [wasmer_h]: ./wasmer.h
|
||||
//! [wasmer_hh]: ./wasmer.hh
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
extern crate wasmer_runtime;
|
||||
extern crate wasmer_runtime_core;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
//! Wasm memory.o
|
||||
//! Create, read, write, grow, destroy memory of an instance.
|
||||
|
||||
use crate::{error::update_last_error, wasmer_limits_t, wasmer_result_t};
|
||||
use libc::{uint32_t, uint8_t};
|
||||
|
@ -1,4 +1,4 @@
|
||||
//! Wasm module.
|
||||
//! Compile, validate, instantiate, serialize, and destroy modules.
|
||||
|
||||
use crate::{
|
||||
error::{update_last_error, CApiError},
|
||||
|
@ -1,4 +1,4 @@
|
||||
//! Wasm tables.
|
||||
//! Create, grow, destroy tables of an instance.
|
||||
|
||||
use crate::{error::update_last_error, wasmer_limits_t, wasmer_result_t};
|
||||
use libc::uint32_t;
|
||||
|
@ -1,4 +1,4 @@
|
||||
//! Wasm values.
|
||||
//! Create and map Rust to WebAssembly values.
|
||||
|
||||
use libc::{int32_t, int64_t};
|
||||
use wasmer_runtime::Value;
|
||||
|
@ -46,7 +46,8 @@ int main()
|
||||
int error_len = wasmer_last_error_length();
|
||||
printf("Error len: `%d`\n", error_len);
|
||||
char *error_str = malloc(error_len);
|
||||
wasmer_last_error_message(error_str, error_len);
|
||||
int error_result = wasmer_last_error_message(error_str, error_len);
|
||||
assert(error_len == error_result);
|
||||
printf("Error str: `%s`\n", error_str);
|
||||
assert(0 == strcmp(error_str, "Call error: Parameters of type [I32] did not match signature [I32, I32] -> [I32]"));
|
||||
free(error_str);
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-runtime-core"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
description = "Wasmer runtime core library"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
|
@ -17,30 +17,31 @@ use crate::{
|
||||
};
|
||||
use std::slice;
|
||||
|
||||
/// The `LocalBacking` "owns" the memory used by all the local resources of an Instance.
|
||||
/// That is, local memories, tables, and globals (as well as some additional
|
||||
/// data for the virtual call machinery).
|
||||
#[derive(Debug)]
|
||||
pub struct LocalBacking {
|
||||
/// This is a map from the local resource index to actual memory,
|
||||
/// table, and globals.
|
||||
pub(crate) memories: BoxedMap<LocalMemoryIndex, Memory>,
|
||||
pub(crate) tables: BoxedMap<LocalTableIndex, Table>,
|
||||
pub(crate) globals: BoxedMap<LocalGlobalIndex, Global>,
|
||||
|
||||
/// This own the memory containing the pointers to the local memories.
|
||||
/// While simplifying implementation, this adds indirection and may hurt
|
||||
/// performance, especially on cache-starved systems.
|
||||
pub(crate) vm_memories: BoxedMap<LocalMemoryIndex, *mut vm::LocalMemory>,
|
||||
pub(crate) vm_tables: BoxedMap<LocalTableIndex, *mut vm::LocalTable>,
|
||||
pub(crate) vm_globals: BoxedMap<LocalGlobalIndex, *mut vm::LocalGlobal>,
|
||||
|
||||
/// The dynamic sigindices are used to efficiently support caching and
|
||||
/// the `call_indirect` wasm instruction. This field (and local_functions
|
||||
/// as well) are subject to change.
|
||||
pub(crate) dynamic_sigindices: BoxedMap<SigIndex, vm::SigId>,
|
||||
pub(crate) local_functions: BoxedMap<LocalFuncIndex, *const vm::Func>,
|
||||
}
|
||||
|
||||
// impl LocalBacking {
|
||||
// pub fn memory(&mut self, local_memory_index: LocalMemoryIndex) -> &mut Memory {
|
||||
// &mut self.memories[local_memory_index]
|
||||
// }
|
||||
|
||||
// pub fn table(&mut self, local_table_index: LocalTableIndex) -> &mut TableBacking {
|
||||
// &mut self.tables[local_table_index]
|
||||
// }
|
||||
// }
|
||||
|
||||
impl LocalBacking {
|
||||
pub(crate) fn new(module: &ModuleInner, imports: &ImportBacking, vmctx: *mut vm::Ctx) -> Self {
|
||||
let mut memories = Self::generate_memories(module);
|
||||
@ -102,6 +103,9 @@ impl LocalBacking {
|
||||
memories.into_boxed_map()
|
||||
}
|
||||
|
||||
/// Initialize each locally-defined memory in the Module.
|
||||
///
|
||||
/// This involves copying in the data initializers.
|
||||
fn finalize_memories(
|
||||
module: &ModuleInner,
|
||||
imports: &ImportBacking,
|
||||
@ -174,6 +178,9 @@ impl LocalBacking {
|
||||
tables.into_boxed_map()
|
||||
}
|
||||
|
||||
/// This initializes all of the locally-defined tables in the Module, e.g.
|
||||
/// putting all the table elements (function pointers)
|
||||
/// in the right places.
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
fn finalize_tables(
|
||||
module: &ModuleInner,
|
||||
|
@ -1,18 +1,19 @@
|
||||
use crate::{
|
||||
backend::RunnableModule,
|
||||
backend::{sys::Memory, Backend, CacheGen, Compiler, CompilerConfig, Token},
|
||||
backend::{Backend, CacheGen, Compiler, CompilerConfig, Token},
|
||||
cache::{Artifact, Error as CacheError},
|
||||
error::{CompileError, CompileResult},
|
||||
module::{ModuleInfo, ModuleInner},
|
||||
parse::LoadError,
|
||||
structures::Map,
|
||||
types::{FuncIndex, FuncSig, SigIndex},
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
use std::marker::PhantomData;
|
||||
use wasmparser::{Operator, Type as WpType};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Event<'a, 'b> {
|
||||
Internal(InternalEvent),
|
||||
Wasm(&'b Operator<'a>),
|
||||
@ -26,6 +27,18 @@ pub enum InternalEvent {
|
||||
GetInternal(u32),
|
||||
}
|
||||
|
||||
impl fmt::Debug for InternalEvent {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
InternalEvent::FunctionBegin(_) => write!(f, "FunctionBegin"),
|
||||
InternalEvent::FunctionEnd => write!(f, "FunctionEnd"),
|
||||
InternalEvent::Breakpoint(_) => write!(f, "Breakpoint"),
|
||||
InternalEvent::SetInternal(_) => write!(f, "SetInternal"),
|
||||
InternalEvent::GetInternal(_) => write!(f, "GetInternal"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BkptInfo {}
|
||||
|
||||
pub trait ModuleCodeGenerator<FCG: FunctionCodeGenerator<E>, RM: RunnableModule, E: Debug> {
|
||||
@ -35,7 +48,7 @@ pub trait ModuleCodeGenerator<FCG: FunctionCodeGenerator<E>, RM: RunnableModule,
|
||||
|
||||
/// Creates a new function and returns the function-scope code generator for it.
|
||||
fn next_function(&mut self) -> Result<&mut FCG, E>;
|
||||
fn finalize(self, module_info: &ModuleInfo) -> Result<RM, E>;
|
||||
fn finalize(self, module_info: &ModuleInfo) -> Result<(RM, Box<dyn CacheGen>), E>;
|
||||
fn feed_signatures(&mut self, signatures: Map<SigIndex, FuncSig>) -> Result<(), E>;
|
||||
|
||||
/// Sets function signatures.
|
||||
@ -44,7 +57,8 @@ pub trait ModuleCodeGenerator<FCG: FunctionCodeGenerator<E>, RM: RunnableModule,
|
||||
/// Adds an import function.
|
||||
fn feed_import_function(&mut self) -> Result<(), E>;
|
||||
|
||||
fn feed_compiler_config(&mut self, config: &CompilerConfig) -> Result<(), E> { Ok(()) }
|
||||
fn feed_compiler_config(&mut self, _config: &CompilerConfig) -> Result<(), E> { Ok(()) }
|
||||
unsafe fn from_cache(cache: Artifact, _: Token) -> Result<ModuleInner, CacheError>;
|
||||
}
|
||||
|
||||
pub struct StreamingCompiler<
|
||||
@ -118,15 +132,6 @@ impl<
|
||||
compiler_config: CompilerConfig,
|
||||
_: Token,
|
||||
) -> CompileResult<ModuleInner> {
|
||||
struct Placeholder;
|
||||
impl CacheGen for Placeholder {
|
||||
fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError> {
|
||||
Err(CacheError::Unknown(
|
||||
"the streaming compiler API doesn't support caching yet".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
let mut mcg = MCG::new();
|
||||
let mut chain = (self.middleware_chain_generator)();
|
||||
let info = crate::parse::read_module(
|
||||
@ -136,22 +141,24 @@ impl<
|
||||
&mut chain,
|
||||
&compiler_config,
|
||||
)?;
|
||||
let exec_context = mcg
|
||||
.finalize(&info)
|
||||
let (exec_context, cache_gen) =
|
||||
mcg.finalize(&info)
|
||||
.map_err(|x| CompileError::InternalError {
|
||||
msg: format!("{:?}", x),
|
||||
})?;
|
||||
Ok(ModuleInner {
|
||||
cache_gen: Box::new(Placeholder),
|
||||
cache_gen,
|
||||
runnable_module: Box::new(exec_context),
|
||||
info: info,
|
||||
info,
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn from_cache(&self, _artifact: Artifact, _: Token) -> Result<ModuleInner, CacheError> {
|
||||
Err(CacheError::Unknown(
|
||||
"the streaming compiler API doesn't support caching yet".to_string(),
|
||||
))
|
||||
unsafe fn from_cache(
|
||||
&self,
|
||||
artifact: Artifact,
|
||||
token: Token,
|
||||
) -> Result<ModuleInner, CacheError> {
|
||||
MCG::from_cache(artifact, token)
|
||||
}
|
||||
}
|
||||
|
||||
@ -248,7 +255,7 @@ pub trait FunctionCodeGenerator<E: Debug> {
|
||||
fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), E>;
|
||||
|
||||
/// Called before the first call to `feed_opcode`.
|
||||
fn begin_body(&mut self) -> Result<(), E>;
|
||||
fn begin_body(&mut self, module_info: &ModuleInfo) -> Result<(), E>;
|
||||
|
||||
/// Called for each operator.
|
||||
fn feed_event(&mut self, op: Event, module_info: &ModuleInfo) -> Result<(), E>;
|
||||
|
@ -11,7 +11,7 @@ pub type ResolveResult<T> = std::result::Result<T, ResolveError>;
|
||||
pub type ParseResult<T> = std::result::Result<T, ParseError>;
|
||||
|
||||
/// This is returned when the chosen compiler is unable to
|
||||
/// successfully compile the provided webassembly module into
|
||||
/// successfully compile the provided WebAssembly module into
|
||||
/// a `Module`.
|
||||
///
|
||||
/// Comparing two `CompileError`s always evaluates to false.
|
||||
@ -114,7 +114,7 @@ impl std::fmt::Display for LinkError {
|
||||
impl std::error::Error for LinkError {}
|
||||
|
||||
/// This is the error type returned when calling
|
||||
/// a webassembly function.
|
||||
/// a WebAssembly function.
|
||||
///
|
||||
/// The main way to do this is `Instance.call`.
|
||||
///
|
||||
@ -270,7 +270,7 @@ impl std::error::Error for CreationError {}
|
||||
|
||||
/// The amalgamation of all errors that can occur
|
||||
/// during the compilation, instantiation, or execution
|
||||
/// of a webassembly module.
|
||||
/// of a WebAssembly module.
|
||||
///
|
||||
/// Comparing two `Error`s always evaluates to false.
|
||||
#[derive(Debug)]
|
||||
|
@ -12,7 +12,7 @@ use crate::{
|
||||
typed_func::{Func, Wasm, WasmTrapInfo, WasmTypeList},
|
||||
types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Type, Value},
|
||||
vm,
|
||||
loader::{self, Loader, Instance as _},
|
||||
loader::Loader,
|
||||
structures::TypedIndex,
|
||||
};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
@ -288,7 +288,7 @@ impl Instance {
|
||||
}
|
||||
}
|
||||
|
||||
/// Call an exported webassembly function given the export name.
|
||||
/// Call an exported WebAssembly function given the export name.
|
||||
/// Pass arguments by wrapping each one in the [`Value`] enum.
|
||||
/// The returned values are also each wrapped in a [`Value`].
|
||||
///
|
||||
@ -296,7 +296,7 @@ impl Instance {
|
||||
///
|
||||
/// # Note:
|
||||
/// This returns `CallResult<Vec<Value>>` in order to support
|
||||
/// the future multi-value returns webassembly feature.
|
||||
/// the future multi-value returns WebAssembly feature.
|
||||
///
|
||||
/// # Usage:
|
||||
/// ```
|
||||
@ -627,7 +627,7 @@ pub struct DynFunc<'a> {
|
||||
}
|
||||
|
||||
impl<'a> DynFunc<'a> {
|
||||
/// Call an exported webassembly function safely.
|
||||
/// Call an exported WebAssembly function safely.
|
||||
///
|
||||
/// Pass arguments by wrapping each one in the [`Value`] enum.
|
||||
/// The returned values are also each wrapped in a [`Value`].
|
||||
@ -636,7 +636,7 @@ impl<'a> DynFunc<'a> {
|
||||
///
|
||||
/// # Note:
|
||||
/// This returns `CallResult<Vec<Value>>` in order to support
|
||||
/// the future multi-value returns webassembly feature.
|
||||
/// the future multi-value returns WebAssembly feature.
|
||||
///
|
||||
/// # Usage:
|
||||
/// ```
|
||||
|
@ -1,3 +1,4 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
#![cfg_attr(nightly, feature(unwind_attributes))]
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -9,7 +9,7 @@ use crate::{
|
||||
types::Value,
|
||||
};
|
||||
use libc::{
|
||||
c_char, mmap, mprotect, munmap, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_NONE, PROT_READ,
|
||||
mmap, mprotect, munmap, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_READ,
|
||||
PROT_WRITE,
|
||||
};
|
||||
|
||||
@ -23,11 +23,11 @@ pub trait Loader {
|
||||
pub trait Instance {
|
||||
type Error: Debug;
|
||||
fn call(&mut self, id: usize, args: &[Value]) -> Result<u64, Self::Error>;
|
||||
fn read_memory(&mut self, offset: u32, len: u32) -> Result<Vec<u8>, Self::Error> {
|
||||
fn read_memory(&mut self, _offset: u32, _len: u32) -> Result<Vec<u8>, Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn write_memory(&mut self, offset: u32, len: u32, buf: &[u8]) -> Result<(), Self::Error> {
|
||||
fn write_memory(&mut self, _offset: u32, _len: u32, _buf: &[u8]) -> Result<(), Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
@ -38,7 +38,7 @@ impl Loader for LocalLoader {
|
||||
type Instance = LocalInstance;
|
||||
type Error = String;
|
||||
|
||||
fn load(&self, rm: &dyn RunnableModule, module: &ModuleInfo, ctx: &Ctx) -> Result<Self::Instance, Self::Error> {
|
||||
fn load(&self, rm: &dyn RunnableModule, _module: &ModuleInfo, _ctx: &Ctx) -> Result<Self::Instance, Self::Error> {
|
||||
let code = rm.get_code().unwrap();
|
||||
let mut code_mem = CodeMemory::new(code.len());
|
||||
code_mem[..code.len()].copy_from_slice(code);
|
||||
|
@ -11,7 +11,7 @@ use crate::{
|
||||
/// This is an internal-only api.
|
||||
///
|
||||
/// A static memory allocates 6GB of *virtual* memory when created
|
||||
/// in order to allow the webassembly module to contain no bounds-checks.
|
||||
/// in order to allow the WebAssembly module to contain no bounds-checks.
|
||||
///
|
||||
/// Additionally, static memories stay at a single virtual address, so there is no need
|
||||
/// to reload its address on each use.
|
||||
|
@ -246,7 +246,7 @@ pub fn read_module<
|
||||
ParserState::CodeOperator(ref op) => {
|
||||
if !body_begun {
|
||||
body_begun = true;
|
||||
fcg.begin_body()
|
||||
fcg.begin_body(&info)
|
||||
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
|
||||
}
|
||||
middlewares
|
||||
|
@ -23,10 +23,18 @@ struct GlobalSigRegistry {
|
||||
sig_assoc: Map<SigIndex, Arc<FuncSig>>,
|
||||
}
|
||||
|
||||
/// The `SigRegistry` represents a process-global map of function signatures
|
||||
/// to signature indexes and vice versa (the map goes both ways).
|
||||
///
|
||||
/// This exists for two reasons:
|
||||
/// 1. The `call_indirect` wasm instruction can compare two signature indices
|
||||
/// to do signature validation very quickly.
|
||||
/// 2. To intern function signatures, which may be expensive to create.
|
||||
#[derive(Debug)]
|
||||
pub struct SigRegistry;
|
||||
|
||||
impl SigRegistry {
|
||||
/// Map a `FuncSig` to a global `SigIndex`.
|
||||
pub fn lookup_sig_index<Sig>(&self, func_sig: Sig) -> SigIndex
|
||||
where
|
||||
Sig: Into<Arc<FuncSig>>,
|
||||
@ -45,11 +53,15 @@ impl SigRegistry {
|
||||
sig_index
|
||||
}
|
||||
|
||||
/// Map a global `SigIndex` to an interned `FuncSig`.
|
||||
pub fn lookup_signature(&self, sig_index: SigIndex) -> Arc<FuncSig> {
|
||||
let global = (*GLOBAL_SIG_REGISTRY).read();
|
||||
Arc::clone(&global.sig_assoc[sig_index])
|
||||
}
|
||||
|
||||
/// Register a function signature with the global signature registry.
|
||||
///
|
||||
/// This will return an interned `FuncSig`.
|
||||
pub fn lookup_signature_ref(&self, func_sig: &FuncSig) -> Arc<FuncSig> {
|
||||
let mut global = (*GLOBAL_SIG_REGISTRY).write();
|
||||
let global = &mut *global;
|
||||
|
@ -33,7 +33,13 @@ impl Memory {
|
||||
|
||||
let protect = protection.to_protect_const();
|
||||
|
||||
let ptr = unsafe { VirtualAlloc(ptr::null_mut(), size, MEM_RESERVE, protect) };
|
||||
let flags = if protection == Protect::None {
|
||||
MEM_RESERVE
|
||||
} else {
|
||||
MEM_RESERVE | MEM_COMMIT
|
||||
};
|
||||
|
||||
let ptr = unsafe { VirtualAlloc(ptr::null_mut(), size, flags, protect) };
|
||||
|
||||
if ptr.is_null() {
|
||||
Err("unable to allocate memory".to_string())
|
||||
@ -229,3 +235,25 @@ fn round_up_to_page_size(size: usize, page_size: usize) -> usize {
|
||||
fn round_down_to_page_size(size: usize, page_size: usize) -> usize {
|
||||
size & !(page_size - 1)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn clone() {
|
||||
// these should work
|
||||
let _ = Memory::with_size_protect(200_000, Protect::Read)
|
||||
.unwrap()
|
||||
.clone();
|
||||
let _ = Memory::with_size_protect(200_000, Protect::ReadWrite)
|
||||
.unwrap()
|
||||
.clone();
|
||||
let _ = Memory::with_size_protect(200_000, Protect::ReadExec)
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
// this would cause segmentation fault as uncommited memory with no access
|
||||
//let _ = Memory::with_size_protect(200_000, Protect::None).unwrap().clone();
|
||||
}
|
||||
}
|
||||
|
@ -247,7 +247,7 @@ impl<A: WasmExternType> WasmTypeList for (A,) {
|
||||
type CStruct = S1<A>;
|
||||
type RetArray = [u64; 1];
|
||||
fn from_ret_array(array: Self::RetArray) -> Self {
|
||||
(WasmExternType::from_native(NativeWasmType::from_bits(
|
||||
(WasmExternType::from_native(NativeWasmType::from_binary(
|
||||
array[0],
|
||||
)),)
|
||||
}
|
||||
@ -274,7 +274,7 @@ impl<A: WasmExternType> WasmTypeList for (A,) {
|
||||
ctx: *mut Ctx,
|
||||
) -> Result<Rets, RuntimeError> {
|
||||
let (a,) = self;
|
||||
let args = [a.to_native().to_bits()];
|
||||
let args = [a.to_native().to_binary()];
|
||||
let mut rets = Rets::empty_ret_array();
|
||||
let mut trap = WasmTrapInfo::Unknown;
|
||||
let mut user_error = None;
|
||||
@ -322,7 +322,7 @@ macro_rules! impl_traits {
|
||||
fn from_ret_array(array: Self::RetArray) -> Self {
|
||||
#[allow(non_snake_case)]
|
||||
let [ $( $x ),* ] = array;
|
||||
( $( WasmExternType::from_native(NativeWasmType::from_bits($x)) ),* )
|
||||
( $( WasmExternType::from_native(NativeWasmType::from_binary($x)) ),* )
|
||||
}
|
||||
fn empty_ret_array() -> Self::RetArray {
|
||||
[0; count_idents!( $( $x ),* )]
|
||||
@ -344,7 +344,7 @@ macro_rules! impl_traits {
|
||||
unsafe fn call<Rets: WasmTypeList>(self, f: NonNull<vm::Func>, wasm: Wasm, ctx: *mut Ctx) -> Result<Rets, RuntimeError> {
|
||||
#[allow(unused_parens)]
|
||||
let ( $( $x ),* ) = self;
|
||||
let args = [ $( $x.to_native().to_bits() ),* ];
|
||||
let args = [ $( $x.to_native().to_binary()),* ];
|
||||
let mut rets = Rets::empty_ret_array();
|
||||
let mut trap = WasmTrapInfo::Unknown;
|
||||
let mut user_error = None;
|
||||
|
@ -85,44 +85,44 @@ where
|
||||
Self: Sized,
|
||||
{
|
||||
const TYPE: Type;
|
||||
fn from_bits(bits: u64) -> Self;
|
||||
fn to_bits(self) -> u64;
|
||||
fn from_binary(bits: u64) -> Self;
|
||||
fn to_binary(self) -> u64;
|
||||
}
|
||||
|
||||
unsafe impl NativeWasmType for i32 {
|
||||
const TYPE: Type = Type::I32;
|
||||
fn from_bits(bits: u64) -> Self {
|
||||
fn from_binary(bits: u64) -> Self {
|
||||
bits as _
|
||||
}
|
||||
fn to_bits(self) -> u64 {
|
||||
fn to_binary(self) -> u64 {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
unsafe impl NativeWasmType for i64 {
|
||||
const TYPE: Type = Type::I64;
|
||||
fn from_bits(bits: u64) -> Self {
|
||||
fn from_binary(bits: u64) -> Self {
|
||||
bits as _
|
||||
}
|
||||
fn to_bits(self) -> u64 {
|
||||
fn to_binary(self) -> u64 {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
unsafe impl NativeWasmType for f32 {
|
||||
const TYPE: Type = Type::F32;
|
||||
fn from_bits(bits: u64) -> Self {
|
||||
bits as _
|
||||
fn from_binary(bits: u64) -> Self {
|
||||
f32::from_bits(bits as u32)
|
||||
}
|
||||
fn to_bits(self) -> u64 {
|
||||
self as _
|
||||
fn to_binary(self) -> u64 {
|
||||
self.to_bits() as _
|
||||
}
|
||||
}
|
||||
unsafe impl NativeWasmType for f64 {
|
||||
const TYPE: Type = Type::F64;
|
||||
fn from_bits(bits: u64) -> Self {
|
||||
bits as _
|
||||
fn from_binary(bits: u64) -> Self {
|
||||
f64::from_bits(bits)
|
||||
}
|
||||
fn to_bits(self) -> u64 {
|
||||
self as _
|
||||
fn to_binary(self) -> u64 {
|
||||
self.to_bits()
|
||||
}
|
||||
}
|
||||
|
||||
@ -410,7 +410,7 @@ pub trait LocalImport {
|
||||
macro_rules! define_map_index {
|
||||
($ty:ident) => {
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct $ty (u32);
|
||||
impl TypedIndex for $ty {
|
||||
#[doc(hidden)]
|
||||
@ -525,3 +525,58 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::types::NativeWasmType;
|
||||
use crate::types::WasmExternType;
|
||||
|
||||
#[test]
|
||||
fn test_native_types_round_trip() {
|
||||
assert_eq!(
|
||||
42i32,
|
||||
i32::from_native(i32::from_binary((42i32).to_native().to_binary()))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
-42i32,
|
||||
i32::from_native(i32::from_binary((-42i32).to_native().to_binary()))
|
||||
);
|
||||
|
||||
use std::i64;
|
||||
let xi64 = i64::MAX;
|
||||
assert_eq!(
|
||||
xi64,
|
||||
i64::from_native(i64::from_binary((xi64).to_native().to_binary()))
|
||||
);
|
||||
let yi64 = i64::MIN;
|
||||
assert_eq!(
|
||||
yi64,
|
||||
i64::from_native(i64::from_binary((yi64).to_native().to_binary()))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
16.5f32,
|
||||
f32::from_native(f32::from_binary((16.5f32).to_native().to_binary()))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
-16.5f32,
|
||||
f32::from_native(f32::from_binary((-16.5f32).to_native().to_binary()))
|
||||
);
|
||||
|
||||
use std::f64;
|
||||
let xf64: f64 = f64::MAX;
|
||||
assert_eq!(
|
||||
xf64,
|
||||
f64::from_native(f64::from_binary((xf64).to_native().to_binary()))
|
||||
);
|
||||
|
||||
let yf64: f64 = f64::MIN;
|
||||
assert_eq!(
|
||||
yf64,
|
||||
f64::from_native(f64::from_binary((yf64).to_native().to_binary()))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,8 +3,7 @@ use crate::{
|
||||
memory::{Memory, MemoryType},
|
||||
module::{ModuleInfo, ModuleInner},
|
||||
structures::TypedIndex,
|
||||
types::{LocalOrImport, MemoryIndex, LocalMemoryIndex},
|
||||
units::{Pages},
|
||||
types::{LocalOrImport, MemoryIndex},
|
||||
vmcalls,
|
||||
};
|
||||
use std::{ffi::c_void, mem, ptr};
|
||||
@ -13,7 +12,16 @@ use hashbrown::HashMap;
|
||||
|
||||
/// The context of the currently running WebAssembly instance.
|
||||
///
|
||||
/// This is implicitly passed to every WebAssembly function.
|
||||
/// Since this is per-instance, each field has a statically
|
||||
/// (as in after compiling the wasm) known size, so no
|
||||
/// runtime checks are necessary.
|
||||
///
|
||||
/// While the runtime currently just passes this around
|
||||
/// as the first, implicit parameter of every function,
|
||||
/// it may someday be pinned to a register (especially
|
||||
/// on arm, which has a ton of registers) to reduce
|
||||
/// register shuffling.
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct Ctx {
|
||||
@ -22,11 +30,25 @@ pub struct Ctx {
|
||||
|
||||
pub(crate) local_functions: *const *const Func,
|
||||
|
||||
/// These are pointers to things that are known to be owned
|
||||
/// by the owning `Instance`.
|
||||
local_backing: *mut LocalBacking,
|
||||
import_backing: *mut ImportBacking,
|
||||
pub module: *const ModuleInner,
|
||||
|
||||
//// This is intended to be user-supplied, per-instance
|
||||
/// contextual data. There are currently some issue with it,
|
||||
/// notably that it cannot be set before running the `start`
|
||||
/// function in a WebAssembly module.
|
||||
///
|
||||
/// [#219](https://github.com/wasmerio/wasmer/pull/219) fixes that
|
||||
/// issue, as well as allowing the user to have *per-function*
|
||||
/// context, instead of just per-instance.
|
||||
pub data: *mut c_void,
|
||||
|
||||
/// If there's a function set in this field, it gets called
|
||||
/// when the context is destructed, e.g. when an `Instance`
|
||||
/// is dropped.
|
||||
pub data_finalizer: Option<fn(data: *mut c_void)>,
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-runtime"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
description = "Wasmer runtime library"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
@ -9,17 +9,17 @@ edition = "2018"
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.3.0", optional = true }
|
||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.1", optional = true }
|
||||
lazy_static = "1.2.0"
|
||||
memmap = "0.7.0"
|
||||
|
||||
[dependencies.wasmer-runtime-core]
|
||||
path = "../runtime-core"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
|
||||
[dependencies.wasmer-clif-backend]
|
||||
path = "../clif-backend"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
optional = true
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -1,3 +1,5 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
//! Wasmer-runtime is a library that makes embedding WebAssembly
|
||||
//! in your application easy, efficient, and safe.
|
||||
//!
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-singlepass-backend"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
repository = "https://github.com/wasmerio/wasmer"
|
||||
description = "Wasmer runtime single pass compiler backend"
|
||||
license = "MIT"
|
||||
@ -8,7 +8,7 @@ authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.3.0" }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" }
|
||||
wasmparser = "0.29.2"
|
||||
dynasm = "0.3.2"
|
||||
dynasmrt = "0.3.1"
|
||||
|
@ -10,18 +10,18 @@ use smallvec::SmallVec;
|
||||
use std::ptr::NonNull;
|
||||
use std::{any::Any, collections::HashMap, sync::Arc};
|
||||
use wasmer_runtime_core::{
|
||||
backend::{Backend, RunnableModule, CompilerConfig, MemoryBoundCheckMode},
|
||||
backend::{sys::Memory, Backend, CacheGen, Token, RunnableModule, CompilerConfig, MemoryBoundCheckMode},
|
||||
cache::{Artifact, Error as CacheError},
|
||||
codegen::*,
|
||||
memory::MemoryType,
|
||||
module::ModuleInfo,
|
||||
module::{ModuleInfo, ModuleInner},
|
||||
structures::{Map, TypedIndex},
|
||||
typed_func::Wasm,
|
||||
types::{
|
||||
FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex,
|
||||
TableIndex, Type,
|
||||
},
|
||||
vm::{self, LocalGlobal, LocalMemory, LocalTable},
|
||||
vmcalls,
|
||||
vm::{self, LocalGlobal, LocalTable},
|
||||
};
|
||||
use wasmparser::{Operator, Type as WpType};
|
||||
|
||||
@ -214,10 +214,10 @@ impl RunnableModule for X64ExecutionContext {
|
||||
user_error: *mut Option<Box<dyn Any>>,
|
||||
num_params_plus_one: Option<NonNull<c_void>>,
|
||||
) -> bool {
|
||||
let rm: &Box<dyn RunnableModule> = &unsafe { &*(*ctx).module }.runnable_module;
|
||||
let execution_context = unsafe {
|
||||
::std::mem::transmute_copy::<&dyn RunnableModule, &X64ExecutionContext>(&&**rm)
|
||||
};
|
||||
let rm: &Box<dyn RunnableModule> = &(&*(*ctx).module).runnable_module;
|
||||
let execution_context =
|
||||
::std::mem::transmute_copy::<&dyn RunnableModule, &X64ExecutionContext>(&&**rm);
|
||||
|
||||
let args = ::std::slice::from_raw_parts(
|
||||
args,
|
||||
num_params_plus_one.unwrap().as_ptr() as usize - 1,
|
||||
@ -365,7 +365,7 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
|
||||
Ok(self.functions.last_mut().unwrap())
|
||||
}
|
||||
|
||||
fn finalize(mut self, _: &ModuleInfo) -> Result<X64ExecutionContext, CodegenError> {
|
||||
fn finalize(mut self, _: &ModuleInfo) -> Result<(X64ExecutionContext, Box<dyn CacheGen>), CodegenError> {
|
||||
let (assembler, breakpoints) = match self.functions.last_mut() {
|
||||
Some(x) => (
|
||||
x.assembler.take().unwrap(),
|
||||
@ -415,7 +415,16 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
|
||||
.collect(),
|
||||
);
|
||||
|
||||
Ok(X64ExecutionContext {
|
||||
struct Placeholder;
|
||||
impl CacheGen for Placeholder {
|
||||
fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError> {
|
||||
Err(CacheError::Unknown(
|
||||
"the singlepass backend doesn't support caching yet".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
Ok((
|
||||
X64ExecutionContext {
|
||||
code: output,
|
||||
functions: self.functions,
|
||||
signatures: self.signatures.as_ref().unwrap().clone(),
|
||||
@ -423,7 +432,9 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
|
||||
func_import_count: self.func_import_count,
|
||||
function_pointers: out_labels,
|
||||
function_offsets: out_offsets,
|
||||
})
|
||||
},
|
||||
Box::new(Placeholder),
|
||||
))
|
||||
}
|
||||
|
||||
fn feed_signatures(&mut self, signatures: Map<SigIndex, FuncSig>) -> Result<(), CodegenError> {
|
||||
@ -480,6 +491,11 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
|
||||
}));
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn from_cache(_artifact: Artifact, _: Token) -> Result<ModuleInner, CacheError> {
|
||||
Err(CacheError::Unknown(
|
||||
"the singlepass compiler API doesn't support caching yet".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl X64FunctionCode {
|
||||
@ -1400,7 +1416,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn begin_body(&mut self) -> Result<(), CodegenError> {
|
||||
fn begin_body(&mut self, _module_info: &ModuleInfo) -> Result<(), CodegenError> {
|
||||
let a = self.assembler.as_mut().unwrap();
|
||||
a.emit_push(Size::S64, Location::GPR(GPR::RBP));
|
||||
a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RBP));
|
||||
@ -4062,7 +4078,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
let (targets, default_target) = table.read_table().unwrap();
|
||||
let cond =
|
||||
get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap());
|
||||
let mut table_label = a.get_label();
|
||||
let table_label = a.get_label();
|
||||
let mut table: Vec<DynamicLabel> = vec![];
|
||||
let default_br = a.get_label();
|
||||
Self::emit_relaxed_binop(
|
||||
@ -4084,7 +4100,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
a.emit_add(Size::S64, Location::GPR(GPR::RCX), Location::GPR(GPR::RDX));
|
||||
a.emit_jmp_location(Location::GPR(GPR::RDX));
|
||||
|
||||
for (i, target) in targets.iter().enumerate() {
|
||||
for target in targets.iter() {
|
||||
let label = a.get_label();
|
||||
a.emit_label(label);
|
||||
table.push(label);
|
||||
|
@ -1,3 +1,4 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
#![feature(proc_macro_hygiene)]
|
||||
|
||||
#[cfg(not(any(
|
||||
|
@ -1,5 +1,5 @@
|
||||
//! Installing signal handlers allows us to handle traps and out-of-bounds memory
|
||||
//! accesses that occur when runniing webassembly.
|
||||
//! accesses that occur when runniing WebAssembly.
|
||||
//!
|
||||
//! This code is inspired by: https://github.com/pepyakin/wasmtime/commit/625a2b6c0815b21996e111da51b9664feb174622
|
||||
//!
|
||||
@ -111,7 +111,7 @@ pub fn call_protected<T>(f: impl FnOnce() -> T) -> Result<T, CallProtError> {
|
||||
// Ok(SIGSEGV) => "segmentation violation",
|
||||
// Ok(SIGBUS) => "bus error",
|
||||
// Err(_) => "error while getting the Signal",
|
||||
// _ => "unkown trapped signal",
|
||||
// _ => "unknown trapped signal",
|
||||
// };
|
||||
// // When the trap-handler is fully implemented, this will return more information.
|
||||
// Err(RuntimeError::Trap {
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-spectests"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
description = "Wasmer spectests library"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
@ -9,10 +9,10 @@ edition = "2018"
|
||||
build = "build/mod.rs"
|
||||
|
||||
[dependencies]
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.3.0" }
|
||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.3.0" }
|
||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.3.0", optional = true }
|
||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.3.0", optional = true }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" }
|
||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.4.1" }
|
||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.4.1", optional = true }
|
||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.1", optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
wabt = "0.7.2"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-wasi"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
description = "Wasmer runtime WASI implementation library"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
@ -8,7 +8,7 @@ repository = "https://github.com/wasmerio/wasmer"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.3.0" }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" }
|
||||
libc = "0.2.50"
|
||||
rand = "0.6.5"
|
||||
# wasmer-runtime-abi = { path = "../runtime-abi" }
|
||||
|
@ -1,3 +1,5 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
|
@ -279,7 +279,7 @@ pub fn fd_allocate(
|
||||
len: __wasi_filesize_t,
|
||||
) -> __wasi_errno_t {
|
||||
debug!("wasi::fd_allocate");
|
||||
unimplemented!()
|
||||
unimplemented!("wasi::fd_allocate")
|
||||
}
|
||||
|
||||
/// ### `fd_close()`
|
||||
@ -434,7 +434,7 @@ pub fn fd_filestat_set_size(
|
||||
st_size: __wasi_filesize_t,
|
||||
) -> __wasi_errno_t {
|
||||
debug!("wasi::fd_filestat_set_size");
|
||||
unimplemented!()
|
||||
unimplemented!("wasi::fd_filestat_set_size")
|
||||
}
|
||||
|
||||
/// ### `fd_filestat_set_times()`
|
||||
@ -474,14 +474,14 @@ pub fn fd_filestat_set_times(
|
||||
inode.stat.st_atim = st_atim;
|
||||
} else if fst_flags & __WASI_FILESTAT_SET_ATIM_NOW != 0 {
|
||||
// set to current real time
|
||||
unimplemented!();
|
||||
unimplemented!("Set filestat time to the current real time");
|
||||
}
|
||||
|
||||
if fst_flags & __WASI_FILESTAT_SET_MTIM != 0 {
|
||||
inode.stat.st_mtim = st_mtim;
|
||||
} else if fst_flags & __WASI_FILESTAT_SET_MTIM_NOW != 0 {
|
||||
// set to current real time
|
||||
unimplemented!();
|
||||
unimplemented!("Set filestat time to the current real time");
|
||||
}
|
||||
|
||||
__WASI_ESUCCESS
|
||||
@ -501,7 +501,7 @@ pub fn fd_pread(
|
||||
let iov_cells = wasi_try!(iovs.deref(memory, 0, iovs_len));
|
||||
let nread_cell = wasi_try!(nread.deref(memory));
|
||||
|
||||
unimplemented!();
|
||||
unimplemented!("wasi::fd_pread");
|
||||
|
||||
__WASI_ESUCCESS
|
||||
}
|
||||
@ -637,7 +637,7 @@ pub fn fd_pwrite(
|
||||
// TODO: verify
|
||||
return __WASI_EISDIR;
|
||||
}
|
||||
Kind::Symlink { .. } => unimplemented!(),
|
||||
Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_pwrite"),
|
||||
Kind::Buffer { buffer } => wasi_try!(write_bytes(
|
||||
&mut buffer[(offset as usize)..],
|
||||
memory,
|
||||
@ -725,7 +725,7 @@ pub fn fd_read(
|
||||
// TODO: verify
|
||||
return __WASI_EISDIR;
|
||||
}
|
||||
Kind::Symlink { .. } => unimplemented!(),
|
||||
Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_read"),
|
||||
Kind::Buffer { buffer } => {
|
||||
wasi_try!(read_bytes(&buffer[offset..], memory, iovs_arr_cell))
|
||||
}
|
||||
@ -771,7 +771,7 @@ pub fn fd_readdir(
|
||||
if let (Ok(buf_arr_cell), Ok(bufused_cell)) =
|
||||
(buf.deref(memory, 0, buf_len), bufused.deref(memory))
|
||||
{
|
||||
unimplemented!()
|
||||
unimplemented!("wasi::fd_readdir")
|
||||
} else {
|
||||
__WASI_EFAULT
|
||||
}
|
||||
@ -833,7 +833,7 @@ pub fn fd_seek(
|
||||
// TODO: handle case if fd is a dir?
|
||||
match whence {
|
||||
__WASI_WHENCE_CUR => fd_entry.offset = (fd_entry.offset as i64 + offset) as u64,
|
||||
__WASI_WHENCE_END => unimplemented!(),
|
||||
__WASI_WHENCE_END => unimplemented!("__WASI__WHENCE_END in wasi::fd_seek"),
|
||||
__WASI_WHENCE_SET => fd_entry.offset = offset as u64,
|
||||
_ => return __WASI_EINVAL,
|
||||
}
|
||||
@ -855,7 +855,7 @@ pub fn fd_seek(
|
||||
pub fn fd_sync(ctx: &mut Ctx, fd: __wasi_fd_t) -> __wasi_errno_t {
|
||||
debug!("wasi::fd_sync");
|
||||
// TODO: check __WASI_RIGHT_FD_SYNC
|
||||
unimplemented!()
|
||||
unimplemented!("wasi::fd_sync")
|
||||
}
|
||||
|
||||
/// ### `fd_tell()`
|
||||
@ -950,7 +950,7 @@ pub fn fd_write(
|
||||
// TODO: verify
|
||||
return __WASI_EISDIR;
|
||||
}
|
||||
Kind::Symlink { .. } => unimplemented!(),
|
||||
Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_write"),
|
||||
Kind::Buffer { buffer } => {
|
||||
wasi_try!(write_bytes(&mut buffer[offset..], memory, iovs_arr_cell))
|
||||
}
|
||||
@ -987,8 +987,62 @@ pub fn path_create_directory(
|
||||
path_len: u32,
|
||||
) -> __wasi_errno_t {
|
||||
debug!("wasi::path_create_directory");
|
||||
// check __WASI_RIGHT_PATH_CREATE_DIRECTORY
|
||||
unimplemented!()
|
||||
let memory = ctx.memory(0);
|
||||
let state = get_wasi_state(ctx);
|
||||
|
||||
let working_dir = wasi_try!(state.fs.fd_map.get(&fd).ok_or(__WASI_EBADF));
|
||||
if !has_rights(working_dir.rights, __WASI_RIGHT_PATH_CREATE_DIRECTORY) {
|
||||
return __WASI_EACCES;
|
||||
}
|
||||
let path_cells = wasi_try!(path.deref(memory, 0, path_len));
|
||||
let path_string =
|
||||
wasi_try!(
|
||||
std::str::from_utf8(unsafe { &*(path_cells as *const [_] as *const [u8]) })
|
||||
.map_err(|_| __WASI_EINVAL)
|
||||
);
|
||||
debug!("=> path: {}", &path_string);
|
||||
|
||||
let path = std::path::PathBuf::from(path_string);
|
||||
let path_vec = wasi_try!(path
|
||||
.components()
|
||||
.map(|comp| {
|
||||
comp.as_os_str()
|
||||
.to_str()
|
||||
.map(|inner_str| inner_str.to_string())
|
||||
.ok_or(__WASI_EINVAL)
|
||||
})
|
||||
.collect::<Result<Vec<String>, __wasi_errno_t>>());
|
||||
if path_vec.is_empty() {
|
||||
return __WASI_EINVAL;
|
||||
}
|
||||
|
||||
assert!(
|
||||
path_vec.len() == 1,
|
||||
"path_create_directory for paths greater than depth 1 has not been implemented because our WASI FS abstractions are a work in progress. We apologize for the inconvenience"
|
||||
);
|
||||
debug!("Path vec: {:#?}", path_vec);
|
||||
|
||||
wasi_try!(std::fs::create_dir(&path).map_err(|_| __WASI_EIO));
|
||||
|
||||
let kind = Kind::Dir {
|
||||
//parent: Some(working_dir.inode),
|
||||
path: path.clone(),
|
||||
entries: Default::default(),
|
||||
};
|
||||
let new_inode = state.fs.inodes.insert(InodeVal {
|
||||
stat: __wasi_filestat_t::default(),
|
||||
is_preopened: false,
|
||||
name: path_vec[0].clone(),
|
||||
kind,
|
||||
});
|
||||
|
||||
if let Kind::Dir { entries, .. } = &mut state.fs.inodes[working_dir.inode].kind {
|
||||
entries.insert(path_vec[0].clone(), new_inode);
|
||||
} else {
|
||||
return __WASI_ENOTDIR;
|
||||
}
|
||||
|
||||
__WASI_ESUCCESS
|
||||
}
|
||||
|
||||
/// ### `path_filestat_get()`
|
||||
@ -1023,32 +1077,71 @@ pub fn path_filestat_get(
|
||||
return __WASI_EACCES;
|
||||
}
|
||||
|
||||
let path_vec = wasi_try!(::std::str::from_utf8(unsafe {
|
||||
let path_string = wasi_try!(::std::str::from_utf8(unsafe {
|
||||
&*(wasi_try!(path.deref(memory, 0, path_len)) as *const [_] as *const [u8])
|
||||
})
|
||||
.map_err(|_| __WASI_EINVAL))
|
||||
.split('/')
|
||||
.map(|str| str.to_string())
|
||||
.map_err(|_| __WASI_EINVAL));
|
||||
debug!("=> path: {}", &path_string);
|
||||
let path = std::path::PathBuf::from(path_string);
|
||||
let path_vec = path
|
||||
.components()
|
||||
.map(|comp| comp.as_os_str().to_string_lossy().to_string())
|
||||
.collect::<Vec<String>>();
|
||||
let buf_cell = wasi_try!(buf.deref(memory));
|
||||
|
||||
if path_vec.is_empty() {
|
||||
return __WASI_EINVAL;
|
||||
}
|
||||
let mut cumulative_path = std::path::PathBuf::new();
|
||||
|
||||
debug!("=> Path vec: {:?}:", &path_vec);
|
||||
// find the inode by traversing the path
|
||||
let mut inode = root_dir.inode;
|
||||
'outer: for segment in path_vec {
|
||||
'outer: for segment in &path_vec[..(path_vec.len() - 1)] {
|
||||
// loop to traverse symlinks
|
||||
// TODO: proper cycle detection
|
||||
let mut sym_count = 0;
|
||||
loop {
|
||||
match &state.fs.inodes[inode].kind {
|
||||
Kind::Dir { entries, .. } => {
|
||||
if let Some(entry) = entries.get(&segment) {
|
||||
cumulative_path.push(&segment);
|
||||
if let Some(entry) = entries.get(segment) {
|
||||
debug!("Entry {:?} found", &segment);
|
||||
inode = entry.clone();
|
||||
continue 'outer;
|
||||
} else {
|
||||
return __WASI_ENOENT;
|
||||
// lazily load
|
||||
debug!("Lazily loading entry {:?}", &segment);
|
||||
let path_metadata =
|
||||
wasi_try!(cumulative_path.metadata().map_err(|_| __WASI_ENOENT));
|
||||
if !path_metadata.is_dir() {
|
||||
// TODO: should this just return invalid arg?
|
||||
return __WASI_ENOTDIR;
|
||||
}
|
||||
let kind = Kind::Dir {
|
||||
path: std::path::PathBuf::from(&segment),
|
||||
entries: Default::default(),
|
||||
};
|
||||
let inode_val = InodeVal::from_file_metadata(
|
||||
&path_metadata,
|
||||
segment.clone(),
|
||||
false,
|
||||
kind,
|
||||
);
|
||||
let new_inode = state.fs.inodes.insert(inode_val);
|
||||
let inode_idx = state.fs.inode_counter.get();
|
||||
state.fs.inode_counter.replace(inode_idx + 1);
|
||||
if let Kind::Dir { entries, .. } = &mut state.fs.inodes[inode].kind {
|
||||
// check that we're not displacing any entries
|
||||
assert!(entries.insert(segment.clone(), new_inode).is_none());
|
||||
state.fs.inodes[new_inode].stat.st_ino = state.fs.inode_counter.get();
|
||||
inode = new_inode;
|
||||
}
|
||||
debug!("Directory {:#?} lazily loaded", &cumulative_path);
|
||||
}
|
||||
continue 'outer;
|
||||
}
|
||||
Kind::Symlink { forwarded } => {
|
||||
// TODO: updated cumulative path
|
||||
sym_count += 1;
|
||||
inode = forwarded.clone();
|
||||
if sym_count > MAX_SYMLINKS {
|
||||
@ -1062,7 +1155,62 @@ pub fn path_filestat_get(
|
||||
}
|
||||
}
|
||||
|
||||
let stat = state.fs.inodes[inode].stat;
|
||||
let final_inode = match &state.fs.inodes[inode].kind {
|
||||
Kind::Dir { path, entries, .. } => {
|
||||
// TODO: fail earlier if size 0
|
||||
let last_segment = path_vec.last().unwrap();
|
||||
cumulative_path.push(last_segment);
|
||||
|
||||
if entries.contains_key(last_segment) {
|
||||
entries[last_segment]
|
||||
} else {
|
||||
// lazily load it if we can
|
||||
if !cumulative_path.exists() {
|
||||
return __WASI_ENOENT;
|
||||
}
|
||||
let final_path_metadata =
|
||||
wasi_try!(cumulative_path.metadata().map_err(|_| __WASI_EIO));
|
||||
let new_inode = if final_path_metadata.is_dir() {
|
||||
debug!("Opening host directory {:#?}", &cumulative_path);
|
||||
state.fs.inodes.insert(InodeVal {
|
||||
stat: __wasi_filestat_t::default(),
|
||||
is_preopened: false, // is this correct?
|
||||
name: last_segment.clone(),
|
||||
kind: Kind::Dir {
|
||||
path: std::path::PathBuf::from(&last_segment),
|
||||
entries: Default::default(),
|
||||
},
|
||||
})
|
||||
} else {
|
||||
debug!("Opening host file {:#?}", &cumulative_path);
|
||||
let real_open_file = wasi_try!(std::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(&cumulative_path)
|
||||
.map_err(|_| __WASI_ENOENT));
|
||||
|
||||
state.fs.inodes.insert(InodeVal {
|
||||
stat: __wasi_filestat_t::default(),
|
||||
is_preopened: false, // is this correct?
|
||||
name: last_segment.clone(),
|
||||
kind: Kind::File {
|
||||
handle: WasiFile::HostFile(real_open_file),
|
||||
},
|
||||
})
|
||||
};
|
||||
// reborrow to insert entry
|
||||
if let Kind::Dir { entries, .. } = &mut state.fs.inodes[inode].kind {
|
||||
entries.insert(last_segment.clone(), new_inode);
|
||||
}
|
||||
new_inode
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return __WASI_ENOTDIR;
|
||||
}
|
||||
};
|
||||
|
||||
let stat = state.fs.inodes[final_inode].stat;
|
||||
|
||||
buf_cell.set(stat);
|
||||
|
||||
@ -1097,7 +1245,7 @@ pub fn path_filestat_set_times(
|
||||
fst_flags: __wasi_fstflags_t,
|
||||
) -> __wasi_errno_t {
|
||||
debug!("wasi::path_filestat_set_times");
|
||||
unimplemented!()
|
||||
unimplemented!("wasi::path_filestat_set_times")
|
||||
}
|
||||
|
||||
/// ### `path_link()`
|
||||
@ -1128,7 +1276,7 @@ pub fn path_link(
|
||||
new_path_len: u32,
|
||||
) -> __wasi_errno_t {
|
||||
debug!("wasi::path_link");
|
||||
unimplemented!()
|
||||
unimplemented!("wasi::path_link")
|
||||
}
|
||||
|
||||
/// ### `path_open()`
|
||||
@ -1221,6 +1369,7 @@ pub fn path_open(
|
||||
match &state.fs.inodes[cur_dir_inode].kind {
|
||||
Kind::Dir { entries, .. } => {
|
||||
if let Some(child) = entries.get(path_segment) {
|
||||
cumulative_path.push(path_segment);
|
||||
let inode_val = *child;
|
||||
cur_dir_inode = inode_val;
|
||||
} else {
|
||||
@ -1376,7 +1525,7 @@ pub fn path_readlink(
|
||||
bufused: WasmPtr<u32>,
|
||||
) -> __wasi_errno_t {
|
||||
debug!("wasi::path_readlink");
|
||||
unimplemented!()
|
||||
unimplemented!("wasi::path_readlink")
|
||||
}
|
||||
pub fn path_remove_directory(
|
||||
ctx: &mut Ctx,
|
||||
@ -1385,7 +1534,7 @@ pub fn path_remove_directory(
|
||||
path_len: u32,
|
||||
) -> __wasi_errno_t {
|
||||
debug!("wasi::path_remove_directory");
|
||||
unimplemented!()
|
||||
unimplemented!("wasi::path_remove_directory")
|
||||
}
|
||||
pub fn path_rename(
|
||||
ctx: &mut Ctx,
|
||||
@ -1397,7 +1546,7 @@ pub fn path_rename(
|
||||
new_path_len: u32,
|
||||
) -> __wasi_errno_t {
|
||||
debug!("wasi::path_rename");
|
||||
unimplemented!()
|
||||
unimplemented!("wasi::path_rename")
|
||||
}
|
||||
pub fn path_symlink(
|
||||
ctx: &mut Ctx,
|
||||
@ -1408,7 +1557,7 @@ pub fn path_symlink(
|
||||
new_path_len: u32,
|
||||
) -> __wasi_errno_t {
|
||||
debug!("wasi::path_symlink");
|
||||
unimplemented!()
|
||||
unimplemented!("wasi::path_symlink")
|
||||
}
|
||||
pub fn path_unlink_file(
|
||||
ctx: &mut Ctx,
|
||||
@ -1417,7 +1566,7 @@ pub fn path_unlink_file(
|
||||
path_len: u32,
|
||||
) -> __wasi_errno_t {
|
||||
debug!("wasi::path_unlink_file");
|
||||
unimplemented!()
|
||||
unimplemented!("wasi::path_unlink_file")
|
||||
}
|
||||
pub fn poll_oneoff(
|
||||
ctx: &mut Ctx,
|
||||
@ -1427,7 +1576,7 @@ pub fn poll_oneoff(
|
||||
nevents: WasmPtr<u32>,
|
||||
) -> __wasi_errno_t {
|
||||
debug!("wasi::poll_oneoff");
|
||||
unimplemented!()
|
||||
unimplemented!("wasi::poll_oneoff")
|
||||
}
|
||||
pub fn proc_exit(ctx: &mut Ctx, code: __wasi_exitcode_t) -> Result<Infallible, ExitCode> {
|
||||
debug!("wasi::proc_exit, {}", code);
|
||||
@ -1435,7 +1584,7 @@ pub fn proc_exit(ctx: &mut Ctx, code: __wasi_exitcode_t) -> Result<Infallible, E
|
||||
}
|
||||
pub fn proc_raise(ctx: &mut Ctx, sig: __wasi_signal_t) -> __wasi_errno_t {
|
||||
debug!("wasi::proc_raise");
|
||||
unimplemented!()
|
||||
unimplemented!("wasi::proc_raise")
|
||||
}
|
||||
|
||||
/// ### `random_get()`
|
||||
@ -1478,7 +1627,7 @@ pub fn sock_recv(
|
||||
ro_flags: WasmPtr<__wasi_roflags_t>,
|
||||
) -> __wasi_errno_t {
|
||||
debug!("wasi::sock_recv");
|
||||
unimplemented!()
|
||||
unimplemented!("wasi::sock_recv")
|
||||
}
|
||||
pub fn sock_send(
|
||||
ctx: &mut Ctx,
|
||||
@ -1489,9 +1638,9 @@ pub fn sock_send(
|
||||
so_datalen: WasmPtr<u32>,
|
||||
) -> __wasi_errno_t {
|
||||
debug!("wasi::sock_send");
|
||||
unimplemented!()
|
||||
unimplemented!("wasi::sock_send")
|
||||
}
|
||||
pub fn sock_shutdown(ctx: &mut Ctx, sock: __wasi_fd_t, how: __wasi_sdflags_t) -> __wasi_errno_t {
|
||||
debug!("wasi::sock_shutdown");
|
||||
unimplemented!()
|
||||
unimplemented!("wasi::sock_shutdown")
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-win-exception-handler"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
description = "Wasmer runtime exception handling for Windows"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
@ -8,7 +8,7 @@ repository = "https://github.com/wasmerio/wasmer"
|
||||
edition = "2018"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.3.0" }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" }
|
||||
winapi = { version = "0.3", features = ["winbase", "errhandlingapi", "minwindef", "minwinbase", "winnt"] }
|
||||
libc = "0.2.49"
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
#define CALL_FIRST 1
|
||||
|
||||
__declspec(thread) jmp_buf jmpBuf;
|
||||
__declspec(thread) DWORD caughtExceptionCode;
|
||||
__declspec(thread) PVOID caughtExceptionAddress;
|
||||
__declspec(thread) DWORD64 caughtInstructionPointer;
|
||||
__declspec(thread) PVOID savedStackPointer;
|
||||
@ -25,6 +26,7 @@ static LONG WINAPI
|
||||
exceptionHandler(struct _EXCEPTION_POINTERS *ExceptionInfo) {
|
||||
EXCEPTION_RECORD* pExceptionRecord = ExceptionInfo->ExceptionRecord;
|
||||
PCONTEXT pCONTEXT = ExceptionInfo->ContextRecord;
|
||||
caughtExceptionCode = pExceptionRecord->ExceptionCode;
|
||||
caughtExceptionAddress = pExceptionRecord->ExceptionAddress;
|
||||
caughtInstructionPointer = pCONTEXT->Rip;
|
||||
if (alreadyHandlingException == TRUE) {
|
||||
@ -61,8 +63,9 @@ uint8_t callProtected(trampoline_t trampoline,
|
||||
}
|
||||
|
||||
// jmp jmp jmp!
|
||||
int signum = setjmp(jmpBuf);
|
||||
if (signum == 0) {
|
||||
int status = setjmp(jmpBuf);
|
||||
if (status == 0) // 0 means the original call
|
||||
{
|
||||
// save the stack pointer
|
||||
savedStackPointer = get_callee_frame_address();
|
||||
trampoline(ctx, func, param_vec, return_vec);
|
||||
@ -74,7 +77,7 @@ uint8_t callProtected(trampoline_t trampoline,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
out_result->code = (uint64_t)signum;
|
||||
out_result->code = (uint64_t)caughtExceptionCode;
|
||||
out_result->exception_address = (uint64_t)caughtExceptionAddress;
|
||||
out_result->instruction_pointer = caughtInstructionPointer;
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
use std::ffi::c_void;
|
||||
use std::ptr::NonNull;
|
||||
use wasmer_runtime_core::vm::{Ctx, Func};
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
#[cfg(windows)]
|
||||
mod exception_handling;
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
extern crate structopt;
|
||||
|
||||
use std::env;
|
||||
@ -87,6 +89,10 @@ struct Run {
|
||||
#[structopt(long = "em-symbol-map", parse(from_os_str), group = "emscripten")]
|
||||
em_symbol_map: Option<PathBuf>,
|
||||
|
||||
/// Begin execution at the specified symbol
|
||||
#[structopt(long = "em-entrypoint", group = "emscripten")]
|
||||
em_entrypoint: Option<String>,
|
||||
|
||||
/// WASI pre-opened directory
|
||||
#[structopt(long = "dir", multiple = true, group = "wasi")]
|
||||
pre_opened_directories: Vec<String>,
|
||||
@ -394,6 +400,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
|
||||
options.path.to_str().unwrap()
|
||||
},
|
||||
options.args.iter().map(|arg| arg.as_str()).collect(),
|
||||
options.em_entrypoint.clone(),
|
||||
)
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
} else {
|
||||
|
@ -18,6 +18,9 @@ DisableWelcomePage=no
|
||||
Source: "..\..\target\release\wasmer.exe"; DestDir: "{app}\bin"
|
||||
Source: "..\..\wapm-cli\target\release\wapm.exe"; DestDir: "{app}\bin"
|
||||
|
||||
[Dirs]
|
||||
Name: "{%USERPROFILE}\.wasmer"
|
||||
|
||||
[Code]
|
||||
const EnvironmentKey = 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment';
|
||||
|
||||
@ -68,7 +71,7 @@ begin
|
||||
if CurStep = ssPostInstall
|
||||
then begin
|
||||
EnvAddPath(ExpandConstant('{app}') +'\bin');
|
||||
EnvAddPath(ExpandConstant('{app}') +'\globals\wapm_packages\.bin');
|
||||
EnvAddPath(ExpandConstant('{%USERPROFILE}') +'\globals\wapm_packages\.bin');
|
||||
end
|
||||
end;
|
||||
|
||||
@ -77,6 +80,6 @@ begin
|
||||
if CurUninstallStep = usPostUninstall
|
||||
then begin
|
||||
EnvRemovePath(ExpandConstant('{app}') +'\bin');
|
||||
EnvAddPath(ExpandConstant('{app}') +'\globals\wapm_packages\.bin');
|
||||
EnvAddPath(ExpandConstant('{%USERPROFILE}') +'\globals\wapm_packages\.bin');
|
||||
end
|
||||
end;
|
@ -1,3 +1,5 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate wasmer_runtime_core;
|
||||
// extern crate wasmer_emscripten;
|
||||
|
@ -1,5 +1,5 @@
|
||||
PREVIOUS_VERSION='0.2.1'
|
||||
NEXT_VERSION='0.3.0'
|
||||
PREVIOUS_VERSION='0.4.0'
|
||||
NEXT_VERSION='0.4.1'
|
||||
|
||||
# quick hack
|
||||
fd Cargo.toml --exec sed -i '' "s/version = \"$PREVIOUS_VERSION\"/version = \"$NEXT_VERSION\"/"
|
||||
|
2
wapm-cli
2
wapm-cli
Submodule wapm-cli updated: c9399f3fb1...8286d0a4bc
Reference in New Issue
Block a user