mirror of
https://github.com/fluencelabs/wasmer
synced 2025-05-09 09:12:13 +00:00
Merge branch 'master' into freebsd
This commit is contained in:
commit
382e244f18
@ -30,7 +30,9 @@ before_deploy:
|
|||||||
- make release-singlepass
|
- make release-singlepass
|
||||||
- mkdir -p artifacts
|
- mkdir -p artifacts
|
||||||
# Make capi
|
# Make capi
|
||||||
- make test-capi-singlepass
|
## Disable capi tests for now:
|
||||||
|
## They are failing because trampolines are not yet implemented for ARM
|
||||||
|
# - make test-capi-singlepass
|
||||||
- make capi-singlepass
|
- make capi-singlepass
|
||||||
- make build-capi-package
|
- make build-capi-package
|
||||||
- cp ./wasmer-c-api.tar.gz ./artifacts/$(./scripts/capi-name.sh)
|
- cp ./wasmer-c-api.tar.gz ./artifacts/$(./scripts/capi-name.sh)
|
||||||
|
22
CHANGELOG.md
22
CHANGELOG.md
@ -2,6 +2,28 @@
|
|||||||
|
|
||||||
## **[Unreleased]**
|
## **[Unreleased]**
|
||||||
|
|
||||||
|
- [#1301](https://github.com/wasmerio/wasmer/pull/1301) Update supported stable Rust version to 1.41.1.
|
||||||
|
- [#1285](https://github.com/wasmerio/wasmer/pull/1285) Greatly improve errors in `wasmer-interface-types`
|
||||||
|
- [#1283](https://github.com/wasmerio/wasmer/pull/1283) Workaround for floating point arguments and return values in `DynamicFunc`s.
|
||||||
|
|
||||||
|
## 0.16.2 - 2020-03-11
|
||||||
|
|
||||||
|
- [#1294](https://github.com/wasmerio/wasmer/pull/1294) Fix bug related to system calls in WASI that rely on reading from WasmPtrs as arrays of length 0. `WasmPtr` will now succeed on length 0 arrays again.
|
||||||
|
|
||||||
|
## 0.16.1 - 2020-03-11
|
||||||
|
|
||||||
|
- [#1291](https://github.com/wasmerio/wasmer/pull/1291) Fix installation packaging script to package the `wax` command.
|
||||||
|
|
||||||
|
## 0.16.0 - 2020-03-11
|
||||||
|
|
||||||
|
- [#1286](https://github.com/wasmerio/wasmer/pull/1286) Updated Windows Wasmer icons. Add wax
|
||||||
|
- [#1284](https://github.com/wasmerio/wasmer/pull/1284) Implement string and memory instructions in `wasmer-interface-types`
|
||||||
|
- [#1272](https://github.com/wasmerio/wasmer/pull/1272) Fix off-by-one error bug when accessing memory with a `WasmPtr` that contains the last valid byte of memory. Also changes the behavior of `WasmPtr<T, Array>` with a length of 0 and `WasmPtr<T>` where `std::mem::size_of::<T>()` is 0 to always return `None`
|
||||||
|
|
||||||
|
## 0.15.0 - 2020-03-04
|
||||||
|
|
||||||
|
- [#1263](https://github.com/wasmerio/wasmer/pull/1263) Changed the behavior of some WASI syscalls to now handle preopened directories more properly. Changed default `--debug` logging to only show Wasmer-related messages.
|
||||||
|
- [#1217](https://github.com/wasmerio/wasmer/pull/1217) Polymorphic host functions based on dynamic trampoline generation.
|
||||||
- [#1252](https://github.com/wasmerio/wasmer/pull/1252) Allow `/` in wasi `--mapdir` wasm path.
|
- [#1252](https://github.com/wasmerio/wasmer/pull/1252) Allow `/` in wasi `--mapdir` wasm path.
|
||||||
- [#1212](https://github.com/wasmerio/wasmer/pull/1212) Add support for GDB JIT debugging:
|
- [#1212](https://github.com/wasmerio/wasmer/pull/1212) Add support for GDB JIT debugging:
|
||||||
- Add `--generate-debug-info` and `-g` flags to `wasmer run` to generate debug information during compilation. The debug info is passed via the GDB JIT interface to a debugger to allow source-level debugging of Wasm files. Currently only available on clif-backend.
|
- Add `--generate-debug-info` and `-g` flags to `wasmer run` to generate debug information during compilation. The debug info is passed via the GDB JIT interface to a debugger to allow source-level debugging of Wasm files. Currently only available on clif-backend.
|
||||||
|
42
Cargo.lock
generated
42
Cargo.lock
generated
@ -659,7 +659,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "inkwell"
|
name = "inkwell"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/TheDan64/inkwell?rev=0a864ebf68b33d4d514b67796264b03898aa0944#0a864ebf68b33d4d514b67796264b03898aa0944"
|
source = "git+https://github.com/TheDan64/inkwell?rev=af4cf4efbb27cdea8a54175ffc18ffd91964618c#af4cf4efbb27cdea8a54175ffc18ffd91964618c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"either",
|
"either",
|
||||||
"inkwell_internals",
|
"inkwell_internals",
|
||||||
@ -673,7 +673,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "inkwell_internals"
|
name = "inkwell_internals"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/TheDan64/inkwell?rev=0a864ebf68b33d4d514b67796264b03898aa0944#0a864ebf68b33d4d514b67796264b03898aa0944"
|
source = "git+https://github.com/TheDan64/inkwell?rev=af4cf4efbb27cdea8a54175ffc18ffd91964618c#af4cf4efbb27cdea8a54175ffc18ffd91964618c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 0.4.30",
|
"proc-macro2 0.4.30",
|
||||||
"quote 0.6.13",
|
"quote 0.6.13",
|
||||||
@ -1820,7 +1820,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmer"
|
name = "wasmer"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"atty",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
@ -1851,7 +1851,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmer-clif-backend"
|
name = "wasmer-clif-backend"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"cranelift-codegen",
|
"cranelift-codegen",
|
||||||
@ -1902,14 +1902,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmer-dev-utils"
|
name = "wasmer-dev-utils"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmer-emscripten"
|
name = "wasmer-emscripten"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"getrandom",
|
"getrandom",
|
||||||
@ -1922,7 +1922,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmer-emscripten-tests"
|
name = "wasmer-emscripten-tests"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glob 0.3.0",
|
"glob 0.3.0",
|
||||||
"wabt",
|
"wabt",
|
||||||
@ -1936,7 +1936,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmer-interface-types"
|
name = "wasmer-interface-types"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nom",
|
"nom",
|
||||||
"wast",
|
"wast",
|
||||||
@ -1952,7 +1952,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmer-llvm-backend"
|
name = "wasmer-llvm-backend"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"cc",
|
"cc",
|
||||||
@ -1983,14 +1983,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmer-middleware-common"
|
name = "wasmer-middleware-common"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"wasmer-runtime-core",
|
"wasmer-runtime-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmer-middleware-common-tests"
|
name = "wasmer-middleware-common-tests"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"criterion",
|
"criterion",
|
||||||
"wabt",
|
"wabt",
|
||||||
@ -2003,7 +2003,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmer-runtime"
|
name = "wasmer-runtime"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"criterion",
|
"criterion",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
@ -2020,7 +2020,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmer-runtime-c-api"
|
name = "wasmer-runtime-c-api"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cbindgen",
|
"cbindgen",
|
||||||
"libc",
|
"libc",
|
||||||
@ -2032,7 +2032,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmer-runtime-core"
|
name = "wasmer-runtime-core"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"blake3",
|
"blake3",
|
||||||
@ -2060,7 +2060,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmer-runtime-core-tests"
|
name = "wasmer-runtime-core-tests"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"wabt",
|
"wabt",
|
||||||
"wasmer-clif-backend",
|
"wasmer-clif-backend",
|
||||||
@ -2071,7 +2071,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmer-singlepass-backend"
|
name = "wasmer-singlepass-backend"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
@ -2088,7 +2088,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmer-spectests"
|
name = "wasmer-spectests"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glob 0.3.0",
|
"glob 0.3.0",
|
||||||
"wabt",
|
"wabt",
|
||||||
@ -2100,7 +2100,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmer-wasi"
|
name = "wasmer-wasi"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
@ -2117,7 +2117,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmer-wasi-experimental-io-devices"
|
name = "wasmer-wasi-experimental-io-devices"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"minifb",
|
"minifb",
|
||||||
@ -2130,7 +2130,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmer-wasi-tests"
|
name = "wasmer-wasi-tests"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glob 0.3.0",
|
"glob 0.3.0",
|
||||||
"wasmer-clif-backend",
|
"wasmer-clif-backend",
|
||||||
@ -2143,7 +2143,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmer-win-exception-handler"
|
name = "wasmer-win-exception-handler"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cmake",
|
"cmake",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wasmer"
|
name = "wasmer"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
repository = "https://github.com/wasmerio/wasmer"
|
repository = "https://github.com/wasmerio/wasmer"
|
||||||
|
6
Makefile
6
Makefile
@ -287,7 +287,9 @@ build-install-package:
|
|||||||
mkdir -p ./install/bin
|
mkdir -p ./install/bin
|
||||||
cp ./wapm-cli/target/release/wapm ./install/bin/
|
cp ./wapm-cli/target/release/wapm ./install/bin/
|
||||||
cp ./target/release/wasmer ./install/bin/
|
cp ./target/release/wasmer ./install/bin/
|
||||||
tar -C ./install -zcvf wasmer.tar.gz bin/wapm bin/wasmer
|
# Create the wax binary as symlink to wapm
|
||||||
|
cd ./install/bin/ && ln -sf wapm wax && chmod +x wax
|
||||||
|
tar -C ./install -zcvf wasmer.tar.gz bin
|
||||||
|
|
||||||
UNAME_S := $(shell uname -s)
|
UNAME_S := $(shell uname -s)
|
||||||
|
|
||||||
@ -315,7 +317,7 @@ endif
|
|||||||
cp lib/runtime-c-api/doc/index.md ./capi/README.md
|
cp lib/runtime-c-api/doc/index.md ./capi/README.md
|
||||||
tar -C ./capi -zcvf wasmer-c-api.tar.gz lib include README.md LICENSE
|
tar -C ./capi -zcvf wasmer-c-api.tar.gz lib include README.md LICENSE
|
||||||
|
|
||||||
WAPM_VERSION = 0.4.3
|
WAPM_VERSION = v0.5.0
|
||||||
build-wapm:
|
build-wapm:
|
||||||
git clone --branch $(WAPM_VERSION) https://github.com/wasmerio/wapm-cli.git
|
git clone --branch $(WAPM_VERSION) https://github.com/wasmerio/wapm-cli.git
|
||||||
cargo build --release --manifest-path wapm-cli/Cargo.toml --features "telemetry update-notifications"
|
cargo build --release --manifest-path wapm-cli/Cargo.toml --features "telemetry update-notifications"
|
||||||
|
@ -20,7 +20,7 @@ jobs:
|
|||||||
- script: cargo fmt --all -- --check
|
- script: cargo fmt --all -- --check
|
||||||
displayName: Lint
|
displayName: Lint
|
||||||
variables:
|
variables:
|
||||||
rust_toolchain: '1.40.0'
|
rust_toolchain: '1.41.1'
|
||||||
|
|
||||||
- job: clippy_lint
|
- job: clippy_lint
|
||||||
pool:
|
pool:
|
||||||
@ -55,7 +55,7 @@ jobs:
|
|||||||
CARGO_HTTP_CHECK_REVOKE: false
|
CARGO_HTTP_CHECK_REVOKE: false
|
||||||
windows:
|
windows:
|
||||||
imageName: "vs2017-win2016"
|
imageName: "vs2017-win2016"
|
||||||
rust_toolchain: '1.40.0'
|
rust_toolchain: '1.41.1'
|
||||||
pool:
|
pool:
|
||||||
vmImage: $(imageName)
|
vmImage: $(imageName)
|
||||||
condition: in(variables['Build.SourceBranch'], 'refs/heads/master', 'refs/heads/staging', 'refs/heads/trying')
|
condition: in(variables['Build.SourceBranch'], 'refs/heads/master', 'refs/heads/staging', 'refs/heads/trying')
|
||||||
@ -114,7 +114,7 @@ jobs:
|
|||||||
MACOSX_DEPLOYMENT_TARGET: 10.10
|
MACOSX_DEPLOYMENT_TARGET: 10.10
|
||||||
windows:
|
windows:
|
||||||
imageName: "vs2017-win2016"
|
imageName: "vs2017-win2016"
|
||||||
rust_toolchain: '1.40.0'
|
rust_toolchain: '1.41.1'
|
||||||
# RUSTFLAGS: -Ctarget-feature=+crt-static
|
# RUSTFLAGS: -Ctarget-feature=+crt-static
|
||||||
pool:
|
pool:
|
||||||
vmImage: $(imageName)
|
vmImage: $(imageName)
|
||||||
@ -183,7 +183,7 @@ jobs:
|
|||||||
MACOSX_DEPLOYMENT_TARGET: 10.10
|
MACOSX_DEPLOYMENT_TARGET: 10.10
|
||||||
windows:
|
windows:
|
||||||
imageName: "vs2017-win2016"
|
imageName: "vs2017-win2016"
|
||||||
rust_toolchain: '1.40.0'
|
rust_toolchain: '1.41.1'
|
||||||
# RUSTFLAGS: -Ctarget-feature=+crt-static
|
# RUSTFLAGS: -Ctarget-feature=+crt-static
|
||||||
pool:
|
pool:
|
||||||
vmImage: $(imageName)
|
vmImage: $(imageName)
|
||||||
@ -300,6 +300,7 @@ jobs:
|
|||||||
isDraft: false
|
isDraft: false
|
||||||
isPreRelease: false
|
isPreRelease: false
|
||||||
assets: '$(Build.ArtifactStagingDirectory)/**'
|
assets: '$(Build.ArtifactStagingDirectory)/**'
|
||||||
|
assetUploadMode: 'replace' # Don't delete previously uploaded assets (default)
|
||||||
|
|
||||||
- job: Publish_Docs
|
- job: Publish_Docs
|
||||||
dependsOn:
|
dependsOn:
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wasmer-clif-backend"
|
name = "wasmer-clif-backend"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
description = "Wasmer runtime Cranelift compiler backend"
|
description = "Wasmer runtime Cranelift compiler backend"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||||
@ -11,7 +11,7 @@ edition = "2018"
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.14.1" }
|
wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" }
|
||||||
cranelift-native = "0.59.0"
|
cranelift-native = "0.59.0"
|
||||||
cranelift-codegen = "0.59.0"
|
cranelift-codegen = "0.59.0"
|
||||||
cranelift-entity = "0.59.0"
|
cranelift-entity = "0.59.0"
|
||||||
@ -38,7 +38,7 @@ version = "0.0.7"
|
|||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winapi = { version = "0.3", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] }
|
winapi = { version = "0.3", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] }
|
||||||
wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.14.1" }
|
wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.16.2" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
generate-debug-information = ["wasm-debug"]
|
generate-debug-information = ["wasm-debug"]
|
||||||
|
@ -209,7 +209,7 @@ impl ModuleCodeGenerator<CraneliftFunctionCodeGenerator, Caller, CodegenError>
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn feed_import_function(&mut self) -> Result<(), CodegenError> {
|
fn feed_import_function(&mut self, _sigindex: SigIndex) -> Result<(), CodegenError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wasmer-dev-utils"
|
name = "wasmer-dev-utils"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
description = "Wasmer runtime core library"
|
description = "Wasmer runtime core library"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wasmer-emscripten-tests"
|
name = "wasmer-emscripten-tests"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
description = "Tests for our Emscripten implementation"
|
description = "Tests for our Emscripten implementation"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||||
@ -9,15 +9,15 @@ publish = false
|
|||||||
build = "build/mod.rs"
|
build = "build/mod.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wasmer-emscripten = { path = "../emscripten", version = "0.14.1" }
|
wasmer-emscripten = { path = "../emscripten", version = "0.16.2" }
|
||||||
wasmer-runtime = { path = "../runtime", version = "0.14.1", default-features = false }
|
wasmer-runtime = { path = "../runtime", version = "0.16.2", default-features = false }
|
||||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.14.1", optional = true}
|
wasmer-clif-backend = { path = "../clif-backend", version = "0.16.2", optional = true}
|
||||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.14.1", optional = true, features = ["test"] }
|
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.2", optional = true, features = ["test"] }
|
||||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.14.1", optional = true }
|
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.2", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
wabt = "0.9.1"
|
wabt = "0.9.1"
|
||||||
wasmer-dev-utils = { path = "../dev-utils", version = "0.14.1"}
|
wasmer-dev-utils = { path = "../dev-utils", version = "0.16.2"}
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
glob = "0.3"
|
glob = "0.3"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wasmer-emscripten"
|
name = "wasmer-emscripten"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
description = "Wasmer runtime emscripten implementation library"
|
description = "Wasmer runtime emscripten implementation library"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||||
@ -15,7 +15,7 @@ lazy_static = "1.4"
|
|||||||
libc = "0.2.60"
|
libc = "0.2.60"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
time = "0.1"
|
time = "0.1"
|
||||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.14.1" }
|
wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" }
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
getrandom = "0.1"
|
getrandom = "0.1"
|
||||||
|
@ -1058,10 +1058,13 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
|
|||||||
pub fn nullfunc(ctx: &mut Ctx, _x: u32) {
|
pub fn nullfunc(ctx: &mut Ctx, _x: u32) {
|
||||||
use crate::process::abort_with_message;
|
use crate::process::abort_with_message;
|
||||||
debug!("emscripten::nullfunc_i {}", _x);
|
debug!("emscripten::nullfunc_i {}", _x);
|
||||||
abort_with_message(ctx, "Invalid function pointer. Perhaps this is an invalid value \
|
abort_with_message(
|
||||||
|
ctx,
|
||||||
|
"Invalid function pointer. Perhaps this is an invalid value \
|
||||||
(e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an \
|
(e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an \
|
||||||
incorrect type, which will fail? (it is worth building your source files with -Werror (\
|
incorrect type, which will fail? (it is worth building your source files with -Werror (\
|
||||||
warnings are errors), as warnings can indicate undefined behavior which can cause this)");
|
warnings are errors), as warnings can indicate undefined behavior which can cause this)",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The current version of this crate
|
/// The current version of this crate
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wasmer-interface-types"
|
name = "wasmer-interface-types"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
description = "WebAssembly Interface Types library for Wasmer"
|
description = "WebAssembly Interface Types library for Wasmer"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||||
|
@ -30,3 +30,68 @@ more](https://github.com/wasmerio/wasmer).
|
|||||||
|
|
||||||
This crate is an implementation of [the living WebAssembly Interface
|
This crate is an implementation of [the living WebAssembly Interface
|
||||||
Types standard](https://github.com/WebAssembly/interface-types).
|
Types standard](https://github.com/WebAssembly/interface-types).
|
||||||
|
|
||||||
|
## Encoders and decoders
|
||||||
|
|
||||||
|
The `wasmer-interface-types` crate comes with an encoder and a decoder
|
||||||
|
for the WAT format, and the binary format, for the WebAssembly
|
||||||
|
Interface Types. An encoder writes an AST into another format, like
|
||||||
|
WAT or binary. A decoder reads an AST from another format, like WAT or
|
||||||
|
binary.
|
||||||
|
|
||||||
|
## Instructions
|
||||||
|
|
||||||
|
Very basically, WebAssembly Interface Types defines a set of
|
||||||
|
instructions, used by adapters to transform the data between
|
||||||
|
WebAssembly core and the outside world ([learn
|
||||||
|
mode](https://github.com/WebAssembly/interface-types/blob/master/proposals/interface-types/Explainer.md)).
|
||||||
|
|
||||||
|
Here is the instructions that are implemented:
|
||||||
|
|
||||||
|
| Instruction | WAT encoder | Binary encoder | WAT decoder | Binary decoder | Interpreter |
|
||||||
|
|-|-|-|-|-|-|
|
||||||
|
| `arg.get` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `call-core` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `memory-to-string` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `string-to-memory` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `call-adapter` | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||||
|
| `defer-call-core` | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||||
|
| `i32-to-s8` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `i32-to-s8x` | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||||||
|
| `i32-to-u8` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `i32-to-s16` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `i32-to-s16x` | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||||||
|
| `i32-to-u16` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `i32-to-s32` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `i32-to-u32` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `i32-to-s64` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `i32-to-u64` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `i64-to-s8` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `i64-to-s8x` | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||||||
|
| `i64-to-u8` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `i64-to-s16` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `i64-to-s16x` | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||||||
|
| `i64-to-u16` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `i64-to-s32` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `i64-to-s32x` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `i64-to-u32` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `i64-to-s64` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `i64-to-u64` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `s8-to-i32` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `u8-to-i32` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `s16-to-i32` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `u16-to-i32` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `s32-to-i32` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `u32-to-i32` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `s64-to-i32` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `s64-to-i32x` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `u64-to-i32` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `u64-to-i32x` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `s8-to-i64` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `u8-to-i64` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `s16-to-i64` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `u16-to-i64` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `s32-to-i64` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `u32-to-i64` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `s64-to-i64` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| `u64-to-i64` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
@ -5,7 +5,7 @@ use crate::interpreter::Instruction;
|
|||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
/// Represents the types supported by WIT.
|
/// Represents the types supported by WIT.
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
pub enum InterfaceType {
|
pub enum InterfaceType {
|
||||||
/// A 8-bits signed integer.
|
/// A 8-bits signed integer.
|
||||||
S8,
|
S8,
|
||||||
@ -51,12 +51,16 @@ pub enum InterfaceType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a type signature.
|
/// Represents a type signature.
|
||||||
|
///
|
||||||
|
/// ```wasm,ignore
|
||||||
|
/// (@interface type (param i32 i32) (result string))
|
||||||
|
/// ```
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
pub struct Type {
|
pub struct Type {
|
||||||
/// Types for the parameters.
|
/// Types for the parameters (`(param …)`).
|
||||||
pub inputs: Vec<InterfaceType>,
|
pub inputs: Vec<InterfaceType>,
|
||||||
|
|
||||||
/// Types for the results.
|
/// Types for the results (`(result …)`).
|
||||||
pub outputs: Vec<InterfaceType>,
|
pub outputs: Vec<InterfaceType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,12 +89,12 @@ pub struct Export<'input> {
|
|||||||
|
|
||||||
/// Represents an adapter.
|
/// Represents an adapter.
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
pub struct Adapter<'input> {
|
pub struct Adapter {
|
||||||
/// The adapter function type.
|
/// The adapter function type.
|
||||||
pub function_type: u32,
|
pub function_type: u32,
|
||||||
|
|
||||||
/// The instructions.
|
/// The instructions.
|
||||||
pub instructions: Vec<Instruction<'input>>,
|
pub instructions: Vec<Instruction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents an implementation.
|
/// Represents an implementation.
|
||||||
@ -133,7 +137,7 @@ pub struct Interfaces<'input> {
|
|||||||
pub imports: Vec<Import<'input>>,
|
pub imports: Vec<Import<'input>>,
|
||||||
|
|
||||||
/// All the adapters.
|
/// All the adapters.
|
||||||
pub adapters: Vec<Adapter<'input>>,
|
pub adapters: Vec<Adapter>,
|
||||||
|
|
||||||
/// All the exported functions.
|
/// All the exported functions.
|
||||||
pub exports: Vec<Export<'input>>,
|
pub exports: Vec<Export<'input>>,
|
||||||
|
@ -168,30 +168,20 @@ fn instruction<'input, E: ParseError<&'input [u8]>>(
|
|||||||
consume!((input, argument_0) = uleb(input)?);
|
consume!((input, argument_0) = uleb(input)?);
|
||||||
(
|
(
|
||||||
input,
|
input,
|
||||||
Instruction::Call {
|
Instruction::CallCore {
|
||||||
function_index: argument_0 as usize,
|
function_index: argument_0 as usize,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
0x02 => {
|
0x03 => (input, Instruction::MemoryToString),
|
||||||
consume!((input, argument_0) = string(input)?);
|
|
||||||
(
|
|
||||||
input,
|
|
||||||
Instruction::CallExport {
|
|
||||||
export_name: argument_0,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
0x03 => (input, Instruction::ReadUtf8),
|
|
||||||
|
|
||||||
0x04 => {
|
0x04 => {
|
||||||
consume!((input, argument_0) = string(input)?);
|
consume!((input, argument_0) = uleb(input)?);
|
||||||
(
|
(
|
||||||
input,
|
input,
|
||||||
Instruction::WriteUtf8 {
|
Instruction::StringToMemory {
|
||||||
allocator_name: argument_0,
|
allocator_index: argument_0 as u32,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -637,12 +627,11 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_instructions() {
|
fn test_instructions() {
|
||||||
let input = &[
|
let input = &[
|
||||||
0x2c, // list of 44 items
|
0x2b, // list of 43 items
|
||||||
0x00, 0x01, // ArgumentGet { index: 1 }
|
0x00, 0x01, // ArgumentGet { index: 1 }
|
||||||
0x01, 0x01, // Call { function_index: 1 }
|
0x01, 0x01, // CallCore { function_index: 1 }
|
||||||
0x02, 0x03, 0x61, 0x62, 0x63, // CallExport { export_name: "abc" }
|
0x03, // MemoryToString
|
||||||
0x03, // ReadUtf8
|
0x04, 0x01, // StringToMemory { allocator_index: 1 }
|
||||||
0x04, 0x03, 0x61, 0x62, 0x63, // WriteUtf8 { allocator_name: "abc" }
|
|
||||||
0x07, // I32ToS8
|
0x07, // I32ToS8
|
||||||
0x08, // I32ToS8X
|
0x08, // I32ToS8X
|
||||||
0x09, // I32ToU8
|
0x09, // I32ToU8
|
||||||
@ -688,12 +677,9 @@ mod tests {
|
|||||||
&[0x0a][..],
|
&[0x0a][..],
|
||||||
vec![
|
vec![
|
||||||
Instruction::ArgumentGet { index: 1 },
|
Instruction::ArgumentGet { index: 1 },
|
||||||
Instruction::Call { function_index: 1 },
|
Instruction::CallCore { function_index: 1 },
|
||||||
Instruction::CallExport { export_name: "abc" },
|
Instruction::MemoryToString,
|
||||||
Instruction::ReadUtf8,
|
Instruction::StringToMemory { allocator_index: 1 },
|
||||||
Instruction::WriteUtf8 {
|
|
||||||
allocator_name: "abc",
|
|
||||||
},
|
|
||||||
Instruction::I32ToS8,
|
Instruction::I32ToS8,
|
||||||
Instruction::I32ToS8X,
|
Instruction::I32ToS8X,
|
||||||
Instruction::I32ToU8,
|
Instruction::I32ToU8,
|
||||||
|
@ -27,10 +27,9 @@ mod keyword {
|
|||||||
|
|
||||||
// Instructions.
|
// Instructions.
|
||||||
custom_keyword!(argument_get = "arg.get");
|
custom_keyword!(argument_get = "arg.get");
|
||||||
custom_keyword!(call);
|
custom_keyword!(call_core = "call-core");
|
||||||
custom_keyword!(call_export = "call-export");
|
custom_keyword!(memory_to_string = "memory-to-string");
|
||||||
custom_keyword!(read_utf8 = "read-utf8");
|
custom_keyword!(string_to_memory = "string-to-memory");
|
||||||
custom_keyword!(write_utf8 = "write-utf8");
|
|
||||||
custom_keyword!(i32_to_s8 = "i32-to-s8");
|
custom_keyword!(i32_to_s8 = "i32-to-s8");
|
||||||
custom_keyword!(i32_to_s8x = "i32-to-s8x");
|
custom_keyword!(i32_to_s8x = "i32-to-s8x");
|
||||||
custom_keyword!(i32_to_u8 = "i32-to-u8");
|
custom_keyword!(i32_to_u8 = "i32-to-u8");
|
||||||
@ -138,7 +137,7 @@ impl Parse<'_> for InterfaceType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parse<'a> for Instruction<'a> {
|
impl<'a> Parse<'a> for Instruction {
|
||||||
#[allow(clippy::cognitive_complexity)]
|
#[allow(clippy::cognitive_complexity)]
|
||||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||||
let mut lookahead = parser.lookahead1();
|
let mut lookahead = parser.lookahead1();
|
||||||
@ -149,27 +148,21 @@ impl<'a> Parse<'a> for Instruction<'a> {
|
|||||||
Ok(Instruction::ArgumentGet {
|
Ok(Instruction::ArgumentGet {
|
||||||
index: parser.parse()?,
|
index: parser.parse()?,
|
||||||
})
|
})
|
||||||
} else if lookahead.peek::<keyword::call>() {
|
} else if lookahead.peek::<keyword::call_core>() {
|
||||||
parser.parse::<keyword::call>()?;
|
parser.parse::<keyword::call_core>()?;
|
||||||
|
|
||||||
Ok(Instruction::Call {
|
Ok(Instruction::CallCore {
|
||||||
function_index: parser.parse::<u64>()? as usize,
|
function_index: parser.parse::<u64>()? as usize,
|
||||||
})
|
})
|
||||||
} else if lookahead.peek::<keyword::call_export>() {
|
} else if lookahead.peek::<keyword::memory_to_string>() {
|
||||||
parser.parse::<keyword::call_export>()?;
|
parser.parse::<keyword::memory_to_string>()?;
|
||||||
|
|
||||||
Ok(Instruction::CallExport {
|
Ok(Instruction::MemoryToString)
|
||||||
export_name: parser.parse()?,
|
} else if lookahead.peek::<keyword::string_to_memory>() {
|
||||||
})
|
parser.parse::<keyword::string_to_memory>()?;
|
||||||
} else if lookahead.peek::<keyword::read_utf8>() {
|
|
||||||
parser.parse::<keyword::read_utf8>()?;
|
|
||||||
|
|
||||||
Ok(Instruction::ReadUtf8)
|
Ok(Instruction::StringToMemory {
|
||||||
} else if lookahead.peek::<keyword::write_utf8>() {
|
allocator_index: parser.parse()?,
|
||||||
parser.parse::<keyword::write_utf8>()?;
|
|
||||||
|
|
||||||
Ok(Instruction::WriteUtf8 {
|
|
||||||
allocator_name: parser.parse()?,
|
|
||||||
})
|
})
|
||||||
} else if lookahead.peek::<keyword::i32_to_s8>() {
|
} else if lookahead.peek::<keyword::i32_to_s8>() {
|
||||||
parser.parse::<keyword::i32_to_s8>()?;
|
parser.parse::<keyword::i32_to_s8>()?;
|
||||||
@ -399,7 +392,7 @@ impl Parse<'_> for FunctionType {
|
|||||||
enum Interface<'a> {
|
enum Interface<'a> {
|
||||||
Type(Type),
|
Type(Type),
|
||||||
Import(Import<'a>),
|
Import(Import<'a>),
|
||||||
Adapter(Adapter<'a>),
|
Adapter(Adapter),
|
||||||
Export(Export<'a>),
|
Export(Export<'a>),
|
||||||
Implementation(Implementation),
|
Implementation(Implementation),
|
||||||
}
|
}
|
||||||
@ -527,7 +520,7 @@ impl<'a> Parse<'a> for Implementation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parse<'a> for Adapter<'a> {
|
impl<'a> Parse<'a> for Adapter {
|
||||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||||
parser.parse::<keyword::func>()?;
|
parser.parse::<keyword::func>()?;
|
||||||
|
|
||||||
@ -673,10 +666,9 @@ mod tests {
|
|||||||
fn test_instructions() {
|
fn test_instructions() {
|
||||||
let inputs = vec![
|
let inputs = vec![
|
||||||
"arg.get 7",
|
"arg.get 7",
|
||||||
"call 7",
|
"call-core 7",
|
||||||
r#"call-export "foo""#,
|
"memory-to-string",
|
||||||
"read-utf8",
|
"string-to-memory 42",
|
||||||
r#"write-utf8 "foo""#,
|
|
||||||
"i32-to-s8",
|
"i32-to-s8",
|
||||||
"i32-to-s8x",
|
"i32-to-s8x",
|
||||||
"i32-to-u8",
|
"i32-to-u8",
|
||||||
@ -719,11 +711,10 @@ mod tests {
|
|||||||
];
|
];
|
||||||
let outputs = vec![
|
let outputs = vec![
|
||||||
Instruction::ArgumentGet { index: 7 },
|
Instruction::ArgumentGet { index: 7 },
|
||||||
Instruction::Call { function_index: 7 },
|
Instruction::CallCore { function_index: 7 },
|
||||||
Instruction::CallExport { export_name: "foo" },
|
Instruction::MemoryToString,
|
||||||
Instruction::ReadUtf8,
|
Instruction::StringToMemory {
|
||||||
Instruction::WriteUtf8 {
|
allocator_index: 42,
|
||||||
allocator_name: "foo",
|
|
||||||
},
|
},
|
||||||
Instruction::I32ToS8,
|
Instruction::I32ToS8,
|
||||||
Instruction::I32ToS8X,
|
Instruction::I32ToS8X,
|
||||||
|
@ -162,7 +162,7 @@ where
|
|||||||
/// Encode an `Adapter` into bytes.
|
/// Encode an `Adapter` into bytes.
|
||||||
///
|
///
|
||||||
/// Decoder is in `decoders::binary::adapters`.
|
/// Decoder is in `decoders::binary::adapters`.
|
||||||
impl<W> ToBytes<W> for Adapter<'_>
|
impl<W> ToBytes<W> for Adapter
|
||||||
where
|
where
|
||||||
W: Write,
|
W: Write,
|
||||||
{
|
{
|
||||||
@ -244,7 +244,7 @@ where
|
|||||||
/// Encode an `Instruction` into bytes.
|
/// Encode an `Instruction` into bytes.
|
||||||
///
|
///
|
||||||
/// Decoder is `decoders::binary::instruction`.
|
/// Decoder is `decoders::binary::instruction`.
|
||||||
impl<W> ToBytes<W> for Instruction<'_>
|
impl<W> ToBytes<W> for Instruction
|
||||||
where
|
where
|
||||||
W: Write,
|
W: Write,
|
||||||
{
|
{
|
||||||
@ -255,21 +255,16 @@ where
|
|||||||
(*index as u64).to_bytes(writer)?;
|
(*index as u64).to_bytes(writer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Instruction::Call { function_index } => {
|
Instruction::CallCore { function_index } => {
|
||||||
0x01_u8.to_bytes(writer)?;
|
0x01_u8.to_bytes(writer)?;
|
||||||
(*function_index as u64).to_bytes(writer)?;
|
(*function_index as u64).to_bytes(writer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Instruction::CallExport { export_name } => {
|
Instruction::MemoryToString => 0x03_u8.to_bytes(writer)?,
|
||||||
0x02_u8.to_bytes(writer)?;
|
|
||||||
export_name.to_bytes(writer)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Instruction::ReadUtf8 => 0x03_u8.to_bytes(writer)?,
|
Instruction::StringToMemory { allocator_index } => {
|
||||||
|
|
||||||
Instruction::WriteUtf8 { allocator_name } => {
|
|
||||||
0x04_u8.to_bytes(writer)?;
|
0x04_u8.to_bytes(writer)?;
|
||||||
allocator_name.to_bytes(writer)?;
|
(*allocator_index as u64).to_bytes(writer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Instruction::I32ToS8 => 0x07_u8.to_bytes(writer)?,
|
Instruction::I32ToS8 => 0x07_u8.to_bytes(writer)?,
|
||||||
@ -554,12 +549,9 @@ mod tests {
|
|||||||
assert_to_bytes!(
|
assert_to_bytes!(
|
||||||
vec![
|
vec![
|
||||||
Instruction::ArgumentGet { index: 1 },
|
Instruction::ArgumentGet { index: 1 },
|
||||||
Instruction::Call { function_index: 1 },
|
Instruction::CallCore { function_index: 1 },
|
||||||
Instruction::CallExport { export_name: "abc" },
|
Instruction::MemoryToString,
|
||||||
Instruction::ReadUtf8,
|
Instruction::StringToMemory { allocator_index: 1 },
|
||||||
Instruction::WriteUtf8 {
|
|
||||||
allocator_name: "abc",
|
|
||||||
},
|
|
||||||
Instruction::I32ToS8,
|
Instruction::I32ToS8,
|
||||||
Instruction::I32ToS8X,
|
Instruction::I32ToS8X,
|
||||||
Instruction::I32ToU8,
|
Instruction::I32ToU8,
|
||||||
@ -601,12 +593,11 @@ mod tests {
|
|||||||
Instruction::U64ToI64,
|
Instruction::U64ToI64,
|
||||||
],
|
],
|
||||||
&[
|
&[
|
||||||
0x2c, // list of 44 items
|
0x2b, // list of 43 items
|
||||||
0x00, 0x01, // ArgumentGet { index: 1 }
|
0x00, 0x01, // ArgumentGet { index: 1 }
|
||||||
0x01, 0x01, // Call { function_index: 1 }
|
0x01, 0x01, // CallCore { function_index: 1 }
|
||||||
0x02, 0x03, 0x61, 0x62, 0x63, // CallExport { export_name: "abc" }
|
0x03, // MemoryToString
|
||||||
0x03, // ReadUtf8
|
0x04, 0x01, // StringToMemory { allocator_index: 1 }
|
||||||
0x04, 0x03, 0x61, 0x62, 0x63, // WriteUtf8 { allocator_name: "abc" }
|
|
||||||
0x07, // I32ToS8
|
0x07, // I32ToS8
|
||||||
0x08, // I32ToS8X
|
0x08, // I32ToS8X
|
||||||
0x09, // I32ToU8
|
0x09, // I32ToU8
|
||||||
|
@ -80,15 +80,14 @@ impl ToString for &InterfaceType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Encode an `Instruction` into a string.
|
/// Encode an `Instruction` into a string.
|
||||||
impl<'input> ToString for &Instruction<'input> {
|
impl ToString for &Instruction {
|
||||||
fn to_string(&self) -> String {
|
fn to_string(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
Instruction::ArgumentGet { index } => format!("arg.get {}", index),
|
Instruction::ArgumentGet { index } => format!("arg.get {}", index),
|
||||||
Instruction::Call { function_index } => format!("call {}", function_index),
|
Instruction::CallCore { function_index } => format!("call-core {}", function_index),
|
||||||
Instruction::CallExport { export_name } => format!(r#"call-export "{}""#, export_name),
|
Instruction::MemoryToString => "memory-to-string".into(),
|
||||||
Instruction::ReadUtf8 => "read-utf8".into(),
|
Instruction::StringToMemory { allocator_index } => {
|
||||||
Instruction::WriteUtf8 { allocator_name } => {
|
format!(r#"string-to-memory {}"#, allocator_index)
|
||||||
format!(r#"write-utf8 "{}""#, allocator_name)
|
|
||||||
}
|
}
|
||||||
Instruction::I32ToS8 => "i32-to-s8".into(),
|
Instruction::I32ToS8 => "i32-to-s8".into(),
|
||||||
Instruction::I32ToS8X => "i32-to-s8x".into(),
|
Instruction::I32ToS8X => "i32-to-s8x".into(),
|
||||||
@ -195,7 +194,7 @@ impl<'input> ToString for &Import<'input> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Encode an `Adapter` into a string.
|
/// Encode an `Adapter` into a string.
|
||||||
impl<'input> ToString for &Adapter<'input> {
|
impl ToString for &Adapter {
|
||||||
fn to_string(&self) -> String {
|
fn to_string(&self) -> String {
|
||||||
format!(
|
format!(
|
||||||
r#"(@interface func (type {function_type}){instructions})"#,
|
r#"(@interface func (type {function_type}){instructions})"#,
|
||||||
@ -361,11 +360,10 @@ mod tests {
|
|||||||
fn test_instructions() {
|
fn test_instructions() {
|
||||||
let inputs: Vec<String> = vec![
|
let inputs: Vec<String> = vec![
|
||||||
(&Instruction::ArgumentGet { index: 7 }).to_string(),
|
(&Instruction::ArgumentGet { index: 7 }).to_string(),
|
||||||
(&Instruction::Call { function_index: 7 }).to_string(),
|
(&Instruction::CallCore { function_index: 7 }).to_string(),
|
||||||
(&Instruction::CallExport { export_name: "foo" }).to_string(),
|
(&Instruction::MemoryToString).to_string(),
|
||||||
(&Instruction::ReadUtf8).to_string(),
|
(&Instruction::StringToMemory {
|
||||||
(&Instruction::WriteUtf8 {
|
allocator_index: 42,
|
||||||
allocator_name: "foo",
|
|
||||||
})
|
})
|
||||||
.to_string(),
|
.to_string(),
|
||||||
(&Instruction::I32ToS8).to_string(),
|
(&Instruction::I32ToS8).to_string(),
|
||||||
@ -410,10 +408,9 @@ mod tests {
|
|||||||
];
|
];
|
||||||
let outputs = vec![
|
let outputs = vec![
|
||||||
"arg.get 7",
|
"arg.get 7",
|
||||||
"call 7",
|
"call-core 7",
|
||||||
r#"call-export "foo""#,
|
"memory-to-string",
|
||||||
"read-utf8",
|
"string-to-memory 42",
|
||||||
r#"write-utf8 "foo""#,
|
|
||||||
"i32-to-s8",
|
"i32-to-s8",
|
||||||
"i32-to-s8x",
|
"i32-to-s8x",
|
||||||
"i32-to-u8",
|
"i32-to-u8",
|
||||||
|
232
lib/interface-types/src/errors.rs
Normal file
232
lib/interface-types/src/errors.rs
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
//! The error module contains all the data structures that represent
|
||||||
|
//! an error.
|
||||||
|
|
||||||
|
use crate::{ast::InterfaceType, interpreter::Instruction};
|
||||||
|
use std::{
|
||||||
|
error::Error,
|
||||||
|
fmt::{self, Display, Formatter},
|
||||||
|
result::Result,
|
||||||
|
string::{self, ToString},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A type alias for instruction's results.
|
||||||
|
pub type InstructionResult<T> = Result<T, InstructionError>;
|
||||||
|
|
||||||
|
/// A type alias for the interpreter result.
|
||||||
|
pub type InterpreterResult<T> = Result<T, InstructionError>;
|
||||||
|
|
||||||
|
/// Structure to represent errors when casting from an `InterfaceType`
|
||||||
|
/// to a native value.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct WasmValueNativeCastError {
|
||||||
|
/// The initial type.
|
||||||
|
pub from: InterfaceType,
|
||||||
|
|
||||||
|
/// The targeted type.
|
||||||
|
///
|
||||||
|
/// `InterfaceType` is used to represent the native type by
|
||||||
|
/// associativity.
|
||||||
|
pub to: InterfaceType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for WasmValueNativeCastError {}
|
||||||
|
|
||||||
|
impl Display for WasmValueNativeCastError {
|
||||||
|
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(formatter, "{:?}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Structure to represent the errors for instructions.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct InstructionError {
|
||||||
|
/// The instruction that raises the error.
|
||||||
|
pub instruction: Instruction,
|
||||||
|
|
||||||
|
/// The error kind.
|
||||||
|
pub error_kind: InstructionErrorKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InstructionError {
|
||||||
|
pub(crate) fn new(instruction: Instruction, error_kind: InstructionErrorKind) -> Self {
|
||||||
|
Self {
|
||||||
|
instruction,
|
||||||
|
error_kind,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for InstructionError {}
|
||||||
|
|
||||||
|
impl Display for InstructionError {
|
||||||
|
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
formatter,
|
||||||
|
"`{}` {}",
|
||||||
|
(&self.instruction).to_string(),
|
||||||
|
self.error_kind
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The kind of instruction errors.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum InstructionErrorKind {
|
||||||
|
/// The instruction needs to read an invocation input at index `index`, but it's missing.
|
||||||
|
InvocationInputIsMissing {
|
||||||
|
/// The invocation input index.
|
||||||
|
index: u32,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Failed to cast from a WIT value to a native value.
|
||||||
|
ToNative(WasmValueNativeCastError),
|
||||||
|
|
||||||
|
/// Failed to cast from `from` to `to`.
|
||||||
|
LoweringLifting {
|
||||||
|
/// The initial type.
|
||||||
|
from: InterfaceType,
|
||||||
|
|
||||||
|
/// The targeted type.
|
||||||
|
to: InterfaceType,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Read a value from the stack, but it doesn't have the expected
|
||||||
|
/// type.
|
||||||
|
InvalidValueOnTheStack {
|
||||||
|
/// The expected type.
|
||||||
|
expected_type: InterfaceType,
|
||||||
|
|
||||||
|
/// The received type.
|
||||||
|
received_type: InterfaceType,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Need to read some values from the stack, but it doesn't
|
||||||
|
/// contain enough data.
|
||||||
|
StackIsTooSmall {
|
||||||
|
/// The number of values that were needed.
|
||||||
|
needed: usize,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// The local or import function doesn't exist.
|
||||||
|
LocalOrImportIsMissing {
|
||||||
|
/// The local or import function index.
|
||||||
|
function_index: u32,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Values given to a local or import function doesn't match the
|
||||||
|
/// function signature.
|
||||||
|
LocalOrImportSignatureMismatch {
|
||||||
|
/// The local or import function index.
|
||||||
|
function_index: u32,
|
||||||
|
|
||||||
|
/// The expected signature.
|
||||||
|
expected: (Vec<InterfaceType>, Vec<InterfaceType>),
|
||||||
|
|
||||||
|
/// The received signature.
|
||||||
|
received: (Vec<InterfaceType>, Vec<InterfaceType>),
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Failed to call a local or import function.
|
||||||
|
LocalOrImportCall {
|
||||||
|
/// The local or import function index that has been called.
|
||||||
|
function_index: u32,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// The memory doesn't exist.
|
||||||
|
MemoryIsMissing {
|
||||||
|
/// The memory indeX.
|
||||||
|
memory_index: u32,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Tried to read out of bounds of the memory.
|
||||||
|
MemoryOutOfBoundsAccess {
|
||||||
|
/// The access index.
|
||||||
|
index: usize,
|
||||||
|
|
||||||
|
/// The memory length.
|
||||||
|
length: usize,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// The string contains invalid UTF-8 encoding.
|
||||||
|
String(string::FromUtf8Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for InstructionErrorKind {}
|
||||||
|
|
||||||
|
impl Display for InstructionErrorKind {
|
||||||
|
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::InvocationInputIsMissing { index } => write!(
|
||||||
|
formatter,
|
||||||
|
"cannot access invocation inputs #{} because it doesn't exist",
|
||||||
|
index
|
||||||
|
),
|
||||||
|
|
||||||
|
Self::ToNative(WasmValueNativeCastError { from, .. }) => write!(
|
||||||
|
formatter,
|
||||||
|
"failed to cast the WIT value `{:?}` to its native type",
|
||||||
|
from,
|
||||||
|
),
|
||||||
|
|
||||||
|
Self::LoweringLifting { from, to } => {
|
||||||
|
write!(formatter, "failed to cast `{:?}` to `{:?}`", from, to)
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::InvalidValueOnTheStack {
|
||||||
|
expected_type,
|
||||||
|
received_type,
|
||||||
|
} => write!(
|
||||||
|
formatter,
|
||||||
|
"read a value of type `{:?}` from the stack, but the type `{:?}` was expected",
|
||||||
|
received_type, expected_type,
|
||||||
|
),
|
||||||
|
|
||||||
|
Self::StackIsTooSmall { needed } => write!(
|
||||||
|
formatter,
|
||||||
|
"needed to read `{}` value(s) from the stack, but it doesn't contain enough data",
|
||||||
|
needed
|
||||||
|
),
|
||||||
|
|
||||||
|
Self::LocalOrImportIsMissing { function_index } => write!(
|
||||||
|
formatter,
|
||||||
|
"the local or import function `{}` doesn't exist",
|
||||||
|
function_index
|
||||||
|
),
|
||||||
|
|
||||||
|
Self::LocalOrImportSignatureMismatch { function_index, expected, received } => write!(
|
||||||
|
formatter,
|
||||||
|
"the local or import function `{}` has the signature `{:?} -> {:?}` but it received values of kind `{:?} -> {:?}`",
|
||||||
|
function_index,
|
||||||
|
expected.0,
|
||||||
|
expected.1,
|
||||||
|
received.0,
|
||||||
|
received.1,
|
||||||
|
),
|
||||||
|
|
||||||
|
Self::LocalOrImportCall { function_index } => write!(
|
||||||
|
formatter,
|
||||||
|
"failed while calling the local or import function `{}`",
|
||||||
|
function_index
|
||||||
|
),
|
||||||
|
|
||||||
|
Self::MemoryIsMissing { memory_index } => write!(
|
||||||
|
formatter,
|
||||||
|
"memory `{}` does not exist",
|
||||||
|
memory_index,
|
||||||
|
),
|
||||||
|
|
||||||
|
Self::MemoryOutOfBoundsAccess { index, length } => write!(
|
||||||
|
formatter,
|
||||||
|
"read out of the memory bounds (index {} > memory length {})",
|
||||||
|
index,
|
||||||
|
length,
|
||||||
|
),
|
||||||
|
|
||||||
|
Self::String(error) => write!(
|
||||||
|
formatter,
|
||||||
|
"{}",
|
||||||
|
error
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,33 +1,27 @@
|
|||||||
//use crate::ast::InterfaceType;
|
//use crate::ast::InterfaceType;
|
||||||
|
|
||||||
/// Represents all the possible WIT instructions.
|
/// Represents all the possible WIT instructions.
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||||
pub enum Instruction<'input> {
|
pub enum Instruction {
|
||||||
/// The `arg.get` instruction.
|
/// The `arg.get` instruction.
|
||||||
ArgumentGet {
|
ArgumentGet {
|
||||||
/// The argument index.
|
/// The argument index.
|
||||||
index: u32,
|
index: u32,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// The `call` instruction.
|
/// The `call-core` instruction.
|
||||||
Call {
|
CallCore {
|
||||||
/// The function index.
|
/// The function index.
|
||||||
function_index: usize,
|
function_index: usize,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// The `call-export` instruction.
|
/// The `memory-to-string` instruction.
|
||||||
CallExport {
|
MemoryToString,
|
||||||
/// The exported function name.
|
|
||||||
export_name: &'input str,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// The `read-utf8` instruction.
|
/// The `string-to-memory` instruction.
|
||||||
ReadUtf8,
|
StringToMemory {
|
||||||
|
/// The allocator function index.
|
||||||
/// The `write-utf8` instruction.
|
allocator_index: u32,
|
||||||
WriteUtf8 {
|
|
||||||
/// The allocator function name.
|
|
||||||
allocator_name: &'input str,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/// The `i32-to-s8,` instruction.
|
/// The `i32-to-s8,` instruction.
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
|
use crate::{
|
||||||
|
errors::{InstructionError, InstructionErrorKind},
|
||||||
|
interpreter::Instruction,
|
||||||
|
};
|
||||||
|
|
||||||
executable_instruction!(
|
executable_instruction!(
|
||||||
argument_get(index: u32, instruction_name: String) -> _ {
|
argument_get(index: u32, instruction: Instruction) -> _ {
|
||||||
move |runtime| -> _ {
|
move |runtime| -> _ {
|
||||||
let invocation_inputs = runtime.invocation_inputs;
|
let invocation_inputs = runtime.invocation_inputs;
|
||||||
|
|
||||||
if index >= (invocation_inputs.len() as u32) {
|
if index >= (invocation_inputs.len() as u32) {
|
||||||
return Err(format!(
|
return Err(InstructionError::new(
|
||||||
"`{}` cannot access argument #{} because it doesn't exist.",
|
instruction,
|
||||||
instruction_name, index
|
InstructionErrorKind::InvocationInputIsMissing { index },
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,6 +54,6 @@ mod tests {
|
|||||||
instructions: [Instruction::ArgumentGet { index: 1 }],
|
instructions: [Instruction::ArgumentGet { index: 1 }],
|
||||||
invocation_inputs: [InterfaceValue::I32(42)],
|
invocation_inputs: [InterfaceValue::I32(42)],
|
||||||
instance: Instance::new(),
|
instance: Instance::new(),
|
||||||
error: "`arg.get 1` cannot access argument #1 because it doesn't exist."
|
error: "`arg.get 1` cannot access invocation inputs #1 because it doesn't exist"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,187 +0,0 @@
|
|||||||
use crate::interpreter::wasm::{
|
|
||||||
structures::{FunctionIndex, TypedIndex},
|
|
||||||
values::InterfaceType,
|
|
||||||
};
|
|
||||||
|
|
||||||
executable_instruction!(
|
|
||||||
call(function_index: usize, instruction_name: String) -> _ {
|
|
||||||
move |runtime| -> _ {
|
|
||||||
let instance = &mut runtime.wasm_instance;
|
|
||||||
let index = FunctionIndex::new(function_index);
|
|
||||||
|
|
||||||
match instance.local_or_import(index) {
|
|
||||||
Some(local_or_import) => {
|
|
||||||
let inputs_cardinality = local_or_import.inputs_cardinality();
|
|
||||||
|
|
||||||
match runtime.stack.pop(inputs_cardinality) {
|
|
||||||
Some(inputs) => {
|
|
||||||
let input_types = inputs
|
|
||||||
.iter()
|
|
||||||
.map(Into::into)
|
|
||||||
.collect::<Vec<InterfaceType>>();
|
|
||||||
|
|
||||||
if input_types != local_or_import.inputs() {
|
|
||||||
return Err(format!(
|
|
||||||
"`{}` cannot call the local or imported function `{}` because the value types on the stack mismatch the function signature (expects {:?}).",
|
|
||||||
instruction_name,
|
|
||||||
function_index,
|
|
||||||
local_or_import.inputs(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
match local_or_import.call(&inputs) {
|
|
||||||
Ok(outputs) => {
|
|
||||||
for output in outputs.iter() {
|
|
||||||
runtime.stack.push(output.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Err(_) => Err(format!(
|
|
||||||
"`{}` failed when calling the local or imported function `{}`.",
|
|
||||||
instruction_name,
|
|
||||||
function_index
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => Err(format!(
|
|
||||||
"`{}` cannot call the local or imported function `{}` because there is not enough data on the stack for the arguments (needs {}).",
|
|
||||||
instruction_name,
|
|
||||||
function_index,
|
|
||||||
inputs_cardinality,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => Err(format!(
|
|
||||||
"`{}` cannot call the local or imported function `{}` because it doesn't exist.",
|
|
||||||
instruction_name,
|
|
||||||
function_index,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
test_executable_instruction!(
|
|
||||||
test_call =
|
|
||||||
instructions: [
|
|
||||||
Instruction::ArgumentGet { index: 1 },
|
|
||||||
Instruction::ArgumentGet { index: 0 },
|
|
||||||
Instruction::Call { function_index: 42 },
|
|
||||||
],
|
|
||||||
invocation_inputs: [
|
|
||||||
InterfaceValue::I32(3),
|
|
||||||
InterfaceValue::I32(4),
|
|
||||||
],
|
|
||||||
instance: Instance::new(),
|
|
||||||
stack: [InterfaceValue::I32(12)],
|
|
||||||
);
|
|
||||||
|
|
||||||
test_executable_instruction!(
|
|
||||||
test_call__invalid_local_import_index =
|
|
||||||
instructions: [
|
|
||||||
Instruction::Call { function_index: 42 },
|
|
||||||
],
|
|
||||||
invocation_inputs: [
|
|
||||||
InterfaceValue::I32(3),
|
|
||||||
InterfaceValue::I32(4),
|
|
||||||
],
|
|
||||||
instance: Default::default(),
|
|
||||||
error: r#"`call 42` cannot call the local or imported function `42` because it doesn't exist."#,
|
|
||||||
);
|
|
||||||
|
|
||||||
test_executable_instruction!(
|
|
||||||
test_call__stack_is_too_small =
|
|
||||||
instructions: [
|
|
||||||
Instruction::ArgumentGet { index: 0 },
|
|
||||||
Instruction::Call { function_index: 42 },
|
|
||||||
// ^^ `42` expects 2 values on the stack, only one is present
|
|
||||||
],
|
|
||||||
invocation_inputs: [
|
|
||||||
InterfaceValue::I32(3),
|
|
||||||
InterfaceValue::I32(4),
|
|
||||||
],
|
|
||||||
instance: Instance::new(),
|
|
||||||
error: r#"`call 42` cannot call the local or imported function `42` because there is not enough data on the stack for the arguments (needs 2)."#,
|
|
||||||
);
|
|
||||||
|
|
||||||
test_executable_instruction!(
|
|
||||||
test_call__invalid_types_in_the_stack =
|
|
||||||
instructions: [
|
|
||||||
Instruction::ArgumentGet { index: 1 },
|
|
||||||
Instruction::ArgumentGet { index: 0 },
|
|
||||||
Instruction::Call { function_index: 42 },
|
|
||||||
],
|
|
||||||
invocation_inputs: [
|
|
||||||
InterfaceValue::I32(3),
|
|
||||||
InterfaceValue::I64(4),
|
|
||||||
// ^^^ mismatch with `42` signature
|
|
||||||
],
|
|
||||||
instance: Instance::new(),
|
|
||||||
error: r#"`call 42` cannot call the local or imported function `42` because the value types on the stack mismatch the function signature (expects [I32, I32])."#,
|
|
||||||
);
|
|
||||||
|
|
||||||
test_executable_instruction!(
|
|
||||||
test_call__failure_when_calling =
|
|
||||||
instructions: [
|
|
||||||
Instruction::ArgumentGet { index: 1 },
|
|
||||||
Instruction::ArgumentGet { index: 0 },
|
|
||||||
Instruction::Call { function_index: 42 },
|
|
||||||
],
|
|
||||||
invocation_inputs: [
|
|
||||||
InterfaceValue::I32(3),
|
|
||||||
InterfaceValue::I32(4),
|
|
||||||
],
|
|
||||||
instance: Instance {
|
|
||||||
locals_or_imports: {
|
|
||||||
let mut hashmap = HashMap::new();
|
|
||||||
hashmap.insert(
|
|
||||||
42,
|
|
||||||
LocalImport {
|
|
||||||
inputs: vec![InterfaceType::I32, InterfaceType::I32],
|
|
||||||
outputs: vec![InterfaceType::I32],
|
|
||||||
function: |_| Err(()),
|
|
||||||
// ^^^^^^^ function fails
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
hashmap
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
error: r#"`call 42` failed when calling the local or imported function `42`."#,
|
|
||||||
);
|
|
||||||
|
|
||||||
test_executable_instruction!(
|
|
||||||
test_call__void =
|
|
||||||
instructions: [
|
|
||||||
Instruction::ArgumentGet { index: 1 },
|
|
||||||
Instruction::ArgumentGet { index: 0 },
|
|
||||||
Instruction::Call { function_index: 42 },
|
|
||||||
],
|
|
||||||
invocation_inputs: [
|
|
||||||
InterfaceValue::I32(3),
|
|
||||||
InterfaceValue::I32(4),
|
|
||||||
],
|
|
||||||
instance: Instance {
|
|
||||||
locals_or_imports: {
|
|
||||||
let mut hashmap = HashMap::new();
|
|
||||||
hashmap.insert(
|
|
||||||
42,
|
|
||||||
LocalImport {
|
|
||||||
inputs: vec![InterfaceType::I32, InterfaceType::I32],
|
|
||||||
outputs: vec![InterfaceType::I32],
|
|
||||||
function: |_| Ok(vec![]),
|
|
||||||
// ^^^^^^^^^^ void
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
hashmap
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
stack: [],
|
|
||||||
);
|
|
||||||
}
|
|
190
lib/interface-types/src/interpreter/instructions/call_core.rs
Normal file
190
lib/interface-types/src/interpreter/instructions/call_core.rs
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
use crate::{
|
||||||
|
errors::{InstructionError, InstructionErrorKind},
|
||||||
|
interpreter::wasm::{
|
||||||
|
structures::{FunctionIndex, TypedIndex},
|
||||||
|
values::InterfaceType,
|
||||||
|
},
|
||||||
|
interpreter::Instruction,
|
||||||
|
};
|
||||||
|
|
||||||
|
executable_instruction!(
|
||||||
|
call_core(function_index: usize, instruction: Instruction) -> _ {
|
||||||
|
move |runtime| -> _ {
|
||||||
|
let instance = &mut runtime.wasm_instance;
|
||||||
|
let index = FunctionIndex::new(function_index);
|
||||||
|
|
||||||
|
let local_or_import = instance.local_or_import(index).ok_or_else(|| {
|
||||||
|
InstructionError::new(
|
||||||
|
instruction,
|
||||||
|
InstructionErrorKind::LocalOrImportIsMissing {
|
||||||
|
function_index: function_index as u32,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let inputs_cardinality = local_or_import.inputs_cardinality();
|
||||||
|
|
||||||
|
let inputs = runtime.stack.pop(inputs_cardinality).ok_or_else(|| {
|
||||||
|
InstructionError::new(
|
||||||
|
instruction,
|
||||||
|
InstructionErrorKind::StackIsTooSmall {
|
||||||
|
needed: inputs_cardinality,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let input_types = inputs
|
||||||
|
.iter()
|
||||||
|
.map(Into::into)
|
||||||
|
.collect::<Vec<InterfaceType>>();
|
||||||
|
|
||||||
|
if input_types != local_or_import.inputs() {
|
||||||
|
return Err(InstructionError::new(
|
||||||
|
instruction,
|
||||||
|
InstructionErrorKind::LocalOrImportSignatureMismatch {
|
||||||
|
function_index: function_index as u32,
|
||||||
|
expected: (local_or_import.inputs().to_vec(), vec![]),
|
||||||
|
received: (input_types, vec![]),
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let outputs = local_or_import.call(&inputs).map_err(|_| {
|
||||||
|
InstructionError::new(
|
||||||
|
instruction,
|
||||||
|
InstructionErrorKind::LocalOrImportCall {
|
||||||
|
function_index: function_index as u32,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
for output in outputs.iter() {
|
||||||
|
runtime.stack.push(output.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_call_core =
|
||||||
|
instructions: [
|
||||||
|
Instruction::ArgumentGet { index: 1 },
|
||||||
|
Instruction::ArgumentGet { index: 0 },
|
||||||
|
Instruction::CallCore { function_index: 42 },
|
||||||
|
],
|
||||||
|
invocation_inputs: [
|
||||||
|
InterfaceValue::I32(3),
|
||||||
|
InterfaceValue::I32(4),
|
||||||
|
],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::I32(12)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_call_core__invalid_local_import_index =
|
||||||
|
instructions: [
|
||||||
|
Instruction::CallCore { function_index: 42 },
|
||||||
|
],
|
||||||
|
invocation_inputs: [
|
||||||
|
InterfaceValue::I32(3),
|
||||||
|
InterfaceValue::I32(4),
|
||||||
|
],
|
||||||
|
instance: Default::default(),
|
||||||
|
error: r#"`call-core 42` the local or import function `42` doesn't exist"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_call_core__stack_is_too_small =
|
||||||
|
instructions: [
|
||||||
|
Instruction::ArgumentGet { index: 0 },
|
||||||
|
Instruction::CallCore { function_index: 42 },
|
||||||
|
// ^^ `42` expects 2 values on the stack, only one is present
|
||||||
|
],
|
||||||
|
invocation_inputs: [
|
||||||
|
InterfaceValue::I32(3),
|
||||||
|
InterfaceValue::I32(4),
|
||||||
|
],
|
||||||
|
instance: Instance::new(),
|
||||||
|
error: r#"`call-core 42` needed to read `2` value(s) from the stack, but it doesn't contain enough data"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_call_core__invalid_types_in_the_stack =
|
||||||
|
instructions: [
|
||||||
|
Instruction::ArgumentGet { index: 1 },
|
||||||
|
Instruction::ArgumentGet { index: 0 },
|
||||||
|
Instruction::CallCore { function_index: 42 },
|
||||||
|
],
|
||||||
|
invocation_inputs: [
|
||||||
|
InterfaceValue::I32(3),
|
||||||
|
InterfaceValue::I64(4),
|
||||||
|
// ^^^ mismatch with `42` signature
|
||||||
|
],
|
||||||
|
instance: Instance::new(),
|
||||||
|
error: r#"`call-core 42` the local or import function `42` has the signature `[I32, I32] -> []` but it received values of kind `[I32, I64] -> []`"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_call_core__failure_when_calling =
|
||||||
|
instructions: [
|
||||||
|
Instruction::ArgumentGet { index: 1 },
|
||||||
|
Instruction::ArgumentGet { index: 0 },
|
||||||
|
Instruction::CallCore { function_index: 42 },
|
||||||
|
],
|
||||||
|
invocation_inputs: [
|
||||||
|
InterfaceValue::I32(3),
|
||||||
|
InterfaceValue::I32(4),
|
||||||
|
],
|
||||||
|
instance: Instance {
|
||||||
|
locals_or_imports: {
|
||||||
|
let mut hashmap = HashMap::new();
|
||||||
|
hashmap.insert(
|
||||||
|
42,
|
||||||
|
LocalImport {
|
||||||
|
inputs: vec![InterfaceType::I32, InterfaceType::I32],
|
||||||
|
outputs: vec![InterfaceType::I32],
|
||||||
|
function: |_| Err(()),
|
||||||
|
// ^^^^^^^ function fails
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
hashmap
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
error: r#"`call-core 42` failed while calling the local or import function `42`"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_call_core__void =
|
||||||
|
instructions: [
|
||||||
|
Instruction::ArgumentGet { index: 1 },
|
||||||
|
Instruction::ArgumentGet { index: 0 },
|
||||||
|
Instruction::CallCore { function_index: 42 },
|
||||||
|
],
|
||||||
|
invocation_inputs: [
|
||||||
|
InterfaceValue::I32(3),
|
||||||
|
InterfaceValue::I32(4),
|
||||||
|
],
|
||||||
|
instance: Instance {
|
||||||
|
locals_or_imports: {
|
||||||
|
let mut hashmap = HashMap::new();
|
||||||
|
hashmap.insert(
|
||||||
|
42,
|
||||||
|
LocalImport {
|
||||||
|
inputs: vec![InterfaceType::I32, InterfaceType::I32],
|
||||||
|
outputs: vec![InterfaceType::I32],
|
||||||
|
function: |_| Ok(vec![]),
|
||||||
|
// ^^^^^^^^^^ void
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
hashmap
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
stack: [],
|
||||||
|
);
|
||||||
|
}
|
@ -1,177 +0,0 @@
|
|||||||
use crate::interpreter::wasm::values::InterfaceType;
|
|
||||||
|
|
||||||
executable_instruction!(
|
|
||||||
call_export(export_name: String, instruction_name: String) -> _ {
|
|
||||||
move |runtime| -> _ {
|
|
||||||
let instance = &mut runtime.wasm_instance;
|
|
||||||
|
|
||||||
match instance.export(&export_name) {
|
|
||||||
Some(export) => {
|
|
||||||
let inputs_cardinality = export.inputs_cardinality();
|
|
||||||
|
|
||||||
match runtime.stack.pop(inputs_cardinality) {
|
|
||||||
Some(inputs) => {
|
|
||||||
let input_types = inputs
|
|
||||||
.iter()
|
|
||||||
.map(Into::into)
|
|
||||||
.collect::<Vec<InterfaceType>>();
|
|
||||||
|
|
||||||
if input_types != export.inputs() {
|
|
||||||
return Err(format!(
|
|
||||||
"`{}` cannot call the exported function `{}` because the value types on the stack mismatch the function signature (expects {:?}).",
|
|
||||||
instruction_name,
|
|
||||||
export_name,
|
|
||||||
export.inputs(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
match export.call(&inputs) {
|
|
||||||
Ok(outputs) => {
|
|
||||||
for output in outputs.iter() {
|
|
||||||
runtime.stack.push(output.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Err(_) => Err(format!(
|
|
||||||
"`{}` failed when calling the exported function `{}`.",
|
|
||||||
instruction_name,
|
|
||||||
export_name
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => Err(format!(
|
|
||||||
"`{}` cannot call the exported function `{}` because there is not enough data on the stack for the arguments (needs {}).",
|
|
||||||
instruction_name,
|
|
||||||
export_name,
|
|
||||||
inputs_cardinality,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => Err(format!(
|
|
||||||
"`{}` cannot call the exported function `{}` because it doesn't exist.",
|
|
||||||
instruction_name,
|
|
||||||
export_name,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
test_executable_instruction!(
|
|
||||||
test_call_export =
|
|
||||||
instructions: [
|
|
||||||
Instruction::ArgumentGet { index: 1 },
|
|
||||||
Instruction::ArgumentGet { index: 0 },
|
|
||||||
Instruction::CallExport { export_name: "sum" },
|
|
||||||
],
|
|
||||||
invocation_inputs: [
|
|
||||||
InterfaceValue::I32(3),
|
|
||||||
InterfaceValue::I32(4),
|
|
||||||
],
|
|
||||||
instance: Instance::new(),
|
|
||||||
stack: [InterfaceValue::I32(7)],
|
|
||||||
);
|
|
||||||
|
|
||||||
test_executable_instruction!(
|
|
||||||
test_call_export__invalid_export_name =
|
|
||||||
instructions: [Instruction::CallExport { export_name: "bar" }],
|
|
||||||
invocation_inputs: [],
|
|
||||||
instance: Instance::new(),
|
|
||||||
error: r#"`call-export "bar"` cannot call the exported function `bar` because it doesn't exist."#,
|
|
||||||
);
|
|
||||||
|
|
||||||
test_executable_instruction!(
|
|
||||||
test_call_export__stack_is_too_small =
|
|
||||||
instructions: [
|
|
||||||
Instruction::ArgumentGet { index: 0 },
|
|
||||||
Instruction::CallExport { export_name: "sum" },
|
|
||||||
],
|
|
||||||
invocation_inputs: [
|
|
||||||
InterfaceValue::I32(3),
|
|
||||||
InterfaceValue::I32(4),
|
|
||||||
],
|
|
||||||
instance: Instance::new(),
|
|
||||||
error: r#"`call-export "sum"` cannot call the exported function `sum` because there is not enough data on the stack for the arguments (needs 2)."#,
|
|
||||||
);
|
|
||||||
|
|
||||||
test_executable_instruction!(
|
|
||||||
test_call_export__invalid_types_in_the_stack =
|
|
||||||
instructions: [
|
|
||||||
Instruction::ArgumentGet { index: 1 },
|
|
||||||
Instruction::ArgumentGet { index: 0 },
|
|
||||||
Instruction::CallExport { export_name: "sum" },
|
|
||||||
],
|
|
||||||
invocation_inputs: [
|
|
||||||
InterfaceValue::I32(3),
|
|
||||||
InterfaceValue::I64(4),
|
|
||||||
// ^^^ mismatch with `sum` signature
|
|
||||||
],
|
|
||||||
instance: Instance::new(),
|
|
||||||
error: r#"`call-export "sum"` cannot call the exported function `sum` because the value types on the stack mismatch the function signature (expects [I32, I32])."#,
|
|
||||||
);
|
|
||||||
|
|
||||||
test_executable_instruction!(
|
|
||||||
test_call_export__failure_when_calling =
|
|
||||||
instructions: [
|
|
||||||
Instruction::ArgumentGet { index: 1 },
|
|
||||||
Instruction::ArgumentGet { index: 0 },
|
|
||||||
Instruction::CallExport { export_name: "sum" },
|
|
||||||
],
|
|
||||||
invocation_inputs: [
|
|
||||||
InterfaceValue::I32(3),
|
|
||||||
InterfaceValue::I32(4),
|
|
||||||
],
|
|
||||||
instance: Instance {
|
|
||||||
exports: {
|
|
||||||
let mut hashmap = HashMap::new();
|
|
||||||
hashmap.insert(
|
|
||||||
"sum".into(),
|
|
||||||
Export {
|
|
||||||
inputs: vec![InterfaceType::I32, InterfaceType::I32],
|
|
||||||
outputs: vec![InterfaceType::I32],
|
|
||||||
function: |_| Err(()),
|
|
||||||
// ^^^^^^^ function fails
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
hashmap
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
error: r#"`call-export "sum"` failed when calling the exported function `sum`."#,
|
|
||||||
);
|
|
||||||
|
|
||||||
test_executable_instruction!(
|
|
||||||
test_call_export__void =
|
|
||||||
instructions: [
|
|
||||||
Instruction::ArgumentGet { index: 1 },
|
|
||||||
Instruction::ArgumentGet { index: 0 },
|
|
||||||
Instruction::CallExport { export_name: "sum" },
|
|
||||||
],
|
|
||||||
invocation_inputs: [
|
|
||||||
InterfaceValue::I32(3),
|
|
||||||
InterfaceValue::I32(4),
|
|
||||||
],
|
|
||||||
instance: Instance {
|
|
||||||
exports: {
|
|
||||||
let mut hashmap = HashMap::new();
|
|
||||||
hashmap.insert(
|
|
||||||
"sum".into(),
|
|
||||||
Export {
|
|
||||||
inputs: vec![InterfaceType::I32, InterfaceType::I32],
|
|
||||||
outputs: vec![InterfaceType::I32],
|
|
||||||
function: |_| Ok(vec![]),
|
|
||||||
// ^^^^^^^^^^ void function
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
hashmap
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
stack: [],
|
|
||||||
);
|
|
||||||
}
|
|
@ -0,0 +1,369 @@
|
|||||||
|
use crate::{
|
||||||
|
ast::InterfaceType,
|
||||||
|
errors::{InstructionError, InstructionErrorKind},
|
||||||
|
interpreter::{wasm::values::InterfaceValue, Instruction},
|
||||||
|
};
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
macro_rules! lowering_lifting {
|
||||||
|
($instruction_function_name:ident, $instruction_name:expr, $from_variant:ident, $to_variant:ident) => {
|
||||||
|
executable_instruction!(
|
||||||
|
$instruction_function_name(instruction: Instruction) -> _ {
|
||||||
|
move |runtime| -> _ {
|
||||||
|
match runtime.stack.pop1() {
|
||||||
|
Some(InterfaceValue::$from_variant(value)) => {
|
||||||
|
runtime
|
||||||
|
.stack
|
||||||
|
.push(InterfaceValue::$to_variant(value.try_into().map_err(
|
||||||
|
|_| {
|
||||||
|
InstructionError::new(
|
||||||
|
instruction,
|
||||||
|
InstructionErrorKind::LoweringLifting {
|
||||||
|
from: InterfaceType::$from_variant,
|
||||||
|
to: InterfaceType::$to_variant
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(wrong_value) => {
|
||||||
|
return Err(InstructionError::new(
|
||||||
|
instruction,
|
||||||
|
InstructionErrorKind::InvalidValueOnTheStack {
|
||||||
|
expected_type: InterfaceType::$from_variant,
|
||||||
|
received_type: (&wrong_value).into(),
|
||||||
|
}
|
||||||
|
))
|
||||||
|
},
|
||||||
|
|
||||||
|
None => {
|
||||||
|
return Err(InstructionError::new(
|
||||||
|
instruction,
|
||||||
|
InstructionErrorKind::StackIsTooSmall { needed: 1 },
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
lowering_lifting!(i32_to_s8, "i32-to-s8", I32, S8);
|
||||||
|
lowering_lifting!(i32_to_u8, "i32-to-u8", I32, U8);
|
||||||
|
lowering_lifting!(i32_to_s16, "i32-to-s16", I32, S16);
|
||||||
|
lowering_lifting!(i32_to_u16, "i32-to-u16", I32, U16);
|
||||||
|
lowering_lifting!(i32_to_s32, "i32-to-s32", I32, S32);
|
||||||
|
lowering_lifting!(i32_to_u32, "i32-to-u32", I32, U32);
|
||||||
|
lowering_lifting!(i32_to_s64, "i32-to-s64", I32, S64);
|
||||||
|
lowering_lifting!(i32_to_u64, "i32-to-u64", I32, U64);
|
||||||
|
lowering_lifting!(i64_to_s8, "i64-to-s8", I64, S8);
|
||||||
|
lowering_lifting!(i64_to_u8, "i64-to-u8", I64, U8);
|
||||||
|
lowering_lifting!(i64_to_s16, "i64-to-s16", I64, S16);
|
||||||
|
lowering_lifting!(i64_to_u16, "i64-to-u16", I64, U16);
|
||||||
|
lowering_lifting!(i64_to_s32, "i64-to-s32", I64, S32);
|
||||||
|
lowering_lifting!(i64_to_u32, "i64-to-u32", I64, U32);
|
||||||
|
lowering_lifting!(i64_to_s64, "i64-to-s64", I64, S64);
|
||||||
|
lowering_lifting!(i64_to_u64, "i64-to-u64", I64, U64);
|
||||||
|
lowering_lifting!(s8_to_i32, "s8-to-i32", S8, I32);
|
||||||
|
lowering_lifting!(u8_to_i32, "u8-to-i32", U8, I32);
|
||||||
|
lowering_lifting!(s16_to_i32, "s16-to-i32", S16, I32);
|
||||||
|
lowering_lifting!(u16_to_i32, "u16-to-i32", U16, I32);
|
||||||
|
lowering_lifting!(s32_to_i32, "s32-to-i32", S32, I32);
|
||||||
|
lowering_lifting!(u32_to_i32, "u32-to-i32", U32, I32);
|
||||||
|
lowering_lifting!(s64_to_i32, "s64-to-i32", S64, I32);
|
||||||
|
lowering_lifting!(u64_to_i32, "u64-to-i32", U64, I32);
|
||||||
|
lowering_lifting!(s8_to_i64, "s8-to-i64", S8, I64);
|
||||||
|
lowering_lifting!(u8_to_i64, "u8-to-i64", U8, I64);
|
||||||
|
lowering_lifting!(s16_to_i64, "s16-to-i64", S16, I64);
|
||||||
|
lowering_lifting!(u16_to_i64, "u16-to-i64", U16, I64);
|
||||||
|
lowering_lifting!(s32_to_i64, "s32-to-i64", S32, I64);
|
||||||
|
lowering_lifting!(u32_to_i64, "u32-to-i64", U32, I64);
|
||||||
|
lowering_lifting!(s64_to_i64, "s64-to-i64", S64, I64);
|
||||||
|
lowering_lifting!(u64_to_i64, "u64-to-i64", U64, I64);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_i32_to_s8 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToS8],
|
||||||
|
invocation_inputs: [InterfaceValue::I32(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::S8(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_convert_fails =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0}, Instruction::I32ToS8],
|
||||||
|
invocation_inputs: [InterfaceValue::I32(128)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
error: "`i32-to-s8` failed to cast `I32` to `S8`"
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_type_mismatch =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0}, Instruction::I32ToS8],
|
||||||
|
invocation_inputs: [InterfaceValue::I64(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
error: "`i32-to-s8` read a value of type `I64` from the stack, but the type `I32` was expected"
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_no_value_on_the_stack =
|
||||||
|
instructions: [Instruction::I32ToS8],
|
||||||
|
invocation_inputs: [InterfaceValue::I32(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
error: "`i32-to-s8` needed to read `1` value(s) from the stack, but it doesn't contain enough data"
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_i32_to_u8 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToU8],
|
||||||
|
invocation_inputs: [InterfaceValue::I32(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::U8(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_i32_to_s16 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToS16],
|
||||||
|
invocation_inputs: [InterfaceValue::I32(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::S16(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_i32_to_u16 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToU16],
|
||||||
|
invocation_inputs: [InterfaceValue::I32(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::U16(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_i32_to_s32 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToS32],
|
||||||
|
invocation_inputs: [InterfaceValue::I32(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::S32(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_i32_to_u32 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToU32],
|
||||||
|
invocation_inputs: [InterfaceValue::I32(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::U32(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_i32_to_s64 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToS64],
|
||||||
|
invocation_inputs: [InterfaceValue::I32(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::S64(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_i32_to_u64 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToU64],
|
||||||
|
invocation_inputs: [InterfaceValue::I32(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::U64(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_i64_to_s8 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64ToS8],
|
||||||
|
invocation_inputs: [InterfaceValue::I64(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::S8(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_i64_to_u8 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64ToU8],
|
||||||
|
invocation_inputs: [InterfaceValue::I64(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::U8(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_i64_to_s16 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64ToS16],
|
||||||
|
invocation_inputs: [InterfaceValue::I64(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::S16(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_i64_to_u16 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64ToU16],
|
||||||
|
invocation_inputs: [InterfaceValue::I64(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::U16(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_i64_to_s32 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64ToS32],
|
||||||
|
invocation_inputs: [InterfaceValue::I64(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::S32(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_i64_to_u32 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64ToU32],
|
||||||
|
invocation_inputs: [InterfaceValue::I64(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::U32(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_i64_to_s64 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64ToS64],
|
||||||
|
invocation_inputs: [InterfaceValue::I64(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::S64(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_i64_to_u64 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64ToU64],
|
||||||
|
invocation_inputs: [InterfaceValue::I64(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::U64(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_s8_to_i32 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S8ToI32],
|
||||||
|
invocation_inputs: [InterfaceValue::S8(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::I32(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_u8_to_i32 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U8ToI32],
|
||||||
|
invocation_inputs: [InterfaceValue::U8(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::I32(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_s16_to_i32 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S16ToI32],
|
||||||
|
invocation_inputs: [InterfaceValue::S16(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::I32(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_u16_to_i32 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U16ToI32],
|
||||||
|
invocation_inputs: [InterfaceValue::U16(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::I32(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_s32_to_i32 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S32ToI32],
|
||||||
|
invocation_inputs: [InterfaceValue::S32(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::I32(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_u32_to_i32 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U32ToI32],
|
||||||
|
invocation_inputs: [InterfaceValue::U32(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::I32(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_s64_to_i32 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S64ToI32],
|
||||||
|
invocation_inputs: [InterfaceValue::S64(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::I32(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_u64_to_i32 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U64ToI32],
|
||||||
|
invocation_inputs: [InterfaceValue::U64(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::I32(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_s8_to_i64 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S8ToI64],
|
||||||
|
invocation_inputs: [InterfaceValue::S8(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::I64(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_u8_to_i64 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U8ToI64],
|
||||||
|
invocation_inputs: [InterfaceValue::U8(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::I64(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_s16_to_i64 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S16ToI64],
|
||||||
|
invocation_inputs: [InterfaceValue::S16(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::I64(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_u16_to_i64 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U16ToI64],
|
||||||
|
invocation_inputs: [InterfaceValue::U16(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::I64(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_s32_to_i64 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S32ToI64],
|
||||||
|
invocation_inputs: [InterfaceValue::S32(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::I64(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_u32_to_i64 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U32ToI64],
|
||||||
|
invocation_inputs: [InterfaceValue::U32(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::I64(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_s64_to_i64 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S64ToI64],
|
||||||
|
invocation_inputs: [InterfaceValue::S64(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::I64(42)],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_u64_to_i64 =
|
||||||
|
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U64ToI64],
|
||||||
|
invocation_inputs: [InterfaceValue::U64(42)],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::I64(42)],
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,159 @@
|
|||||||
|
use super::to_native;
|
||||||
|
use crate::{
|
||||||
|
errors::{InstructionError, InstructionErrorKind},
|
||||||
|
interpreter::{wasm::values::InterfaceValue, Instruction},
|
||||||
|
};
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
|
executable_instruction!(
|
||||||
|
memory_to_string(instruction: Instruction) -> _ {
|
||||||
|
move |runtime| -> _ {
|
||||||
|
let inputs = runtime.stack.pop(2).ok_or_else(|| {
|
||||||
|
InstructionError::new(
|
||||||
|
instruction,
|
||||||
|
InstructionErrorKind::StackIsTooSmall { needed: 2 },
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let memory_index: u32 = 0;
|
||||||
|
|
||||||
|
let memory = runtime
|
||||||
|
.wasm_instance
|
||||||
|
.memory(memory_index as usize)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
InstructionError::new(
|
||||||
|
instruction,
|
||||||
|
InstructionErrorKind::MemoryIsMissing { memory_index },
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let length = to_native::<i32>(&inputs[0], instruction)? as usize;
|
||||||
|
let pointer = to_native::<i32>(&inputs[1], instruction)? as usize;
|
||||||
|
let memory_view = memory.view();
|
||||||
|
|
||||||
|
if length == 0 {
|
||||||
|
runtime.stack.push(InterfaceValue::String("".into()));
|
||||||
|
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
if memory_view.len() <= pointer + length - 1 {
|
||||||
|
return Err(InstructionError::new(
|
||||||
|
instruction,
|
||||||
|
InstructionErrorKind::MemoryOutOfBoundsAccess {
|
||||||
|
index: pointer + length,
|
||||||
|
length: memory_view.len(),
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let data: Vec<u8> = (&memory_view[pointer..=pointer + length - 1])
|
||||||
|
.iter()
|
||||||
|
.map(Cell::get)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let string = String::from_utf8(data)
|
||||||
|
.map_err(|error| InstructionError::new(instruction, InstructionErrorKind::String(error)))?;
|
||||||
|
|
||||||
|
runtime.stack.push(InterfaceValue::String(string));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_memory_to_string =
|
||||||
|
instructions: [
|
||||||
|
Instruction::ArgumentGet { index: 1 },
|
||||||
|
Instruction::ArgumentGet { index: 0 },
|
||||||
|
Instruction::MemoryToString,
|
||||||
|
],
|
||||||
|
invocation_inputs: [
|
||||||
|
InterfaceValue::I32(13),
|
||||||
|
// ^^^^^^^ length
|
||||||
|
InterfaceValue::I32(0),
|
||||||
|
// ^^^^^^ pointer
|
||||||
|
],
|
||||||
|
instance: Instance {
|
||||||
|
memory: Memory::new("Hello, World!".as_bytes().iter().map(|u| Cell::new(*u)).collect()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
stack: [InterfaceValue::String("Hello, World!".into())],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_memory_to_string__empty_string =
|
||||||
|
instructions: [
|
||||||
|
Instruction::ArgumentGet { index: 1 },
|
||||||
|
Instruction::ArgumentGet { index: 0 },
|
||||||
|
Instruction::MemoryToString,
|
||||||
|
],
|
||||||
|
invocation_inputs: [
|
||||||
|
InterfaceValue::I32(0),
|
||||||
|
InterfaceValue::I32(0),
|
||||||
|
],
|
||||||
|
instance: Instance {
|
||||||
|
memory: Memory::new(vec![]),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
stack: [InterfaceValue::String("".into())],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_memory_to_string__read_out_of_memory =
|
||||||
|
instructions: [
|
||||||
|
Instruction::ArgumentGet { index: 1 },
|
||||||
|
Instruction::ArgumentGet { index: 0 },
|
||||||
|
Instruction::MemoryToString,
|
||||||
|
],
|
||||||
|
invocation_inputs: [
|
||||||
|
InterfaceValue::I32(13),
|
||||||
|
// ^^^^^^^ length is too long
|
||||||
|
InterfaceValue::I32(0),
|
||||||
|
// ^^^^^^ pointer
|
||||||
|
],
|
||||||
|
instance: Instance {
|
||||||
|
memory: Memory::new("Hello!".as_bytes().iter().map(|u| Cell::new(*u)).collect()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
error: r#"`memory-to-string` read out of the memory bounds (index 13 > memory length 6)"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_memory_to_string__invalid_encoding =
|
||||||
|
instructions: [
|
||||||
|
Instruction::ArgumentGet { index: 1 },
|
||||||
|
Instruction::ArgumentGet { index: 0 },
|
||||||
|
Instruction::MemoryToString,
|
||||||
|
],
|
||||||
|
invocation_inputs: [
|
||||||
|
InterfaceValue::I32(4),
|
||||||
|
// ^^^^^^ length is too long
|
||||||
|
InterfaceValue::I32(0),
|
||||||
|
// ^^^^^^ pointer
|
||||||
|
],
|
||||||
|
instance: Instance {
|
||||||
|
memory: Memory::new(vec![0, 159, 146, 150].iter().map(|b| Cell::new(*b)).collect::<Vec<Cell<u8>>>()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
error: r#"`memory-to-string` invalid utf-8 sequence of 1 bytes from index 1"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_memory_to_string__stack_is_too_small =
|
||||||
|
instructions: [
|
||||||
|
Instruction::ArgumentGet { index: 0 },
|
||||||
|
Instruction::MemoryToString,
|
||||||
|
// ^^^^^^^^^^^^^^ `memory-to-string` expects 2 values on the stack, only one is present.
|
||||||
|
],
|
||||||
|
invocation_inputs: [
|
||||||
|
InterfaceValue::I32(13),
|
||||||
|
InterfaceValue::I32(0),
|
||||||
|
],
|
||||||
|
instance: Instance::new(),
|
||||||
|
error: r#"`memory-to-string` needed to read `2` value(s) from the stack, but it doesn't contain enough data"#,
|
||||||
|
);
|
||||||
|
}
|
@ -1,14 +1,35 @@
|
|||||||
mod argument_get;
|
mod argument_get;
|
||||||
mod call;
|
mod call_core;
|
||||||
mod call_export;
|
mod lowering_lifting;
|
||||||
mod read_utf8;
|
mod memory_to_string;
|
||||||
mod write_utf8;
|
mod string_to_memory;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
errors::{InstructionError, InstructionErrorKind, InstructionResult, WasmValueNativeCastError},
|
||||||
|
interpreter::{
|
||||||
|
wasm::values::{InterfaceValue, NativeType},
|
||||||
|
Instruction,
|
||||||
|
},
|
||||||
|
};
|
||||||
pub(crate) use argument_get::argument_get;
|
pub(crate) use argument_get::argument_get;
|
||||||
pub(crate) use call::call;
|
pub(crate) use call_core::call_core;
|
||||||
pub(crate) use call_export::call_export;
|
pub(crate) use lowering_lifting::*;
|
||||||
pub(crate) use read_utf8::read_utf8;
|
pub(crate) use memory_to_string::memory_to_string;
|
||||||
pub(crate) use write_utf8::write_utf8;
|
use std::convert::TryFrom;
|
||||||
|
pub(crate) use string_to_memory::string_to_memory;
|
||||||
|
|
||||||
|
/// Just a short helper to map the error of a cast from an
|
||||||
|
/// `InterfaceValue` to a native value.
|
||||||
|
pub(crate) fn to_native<'a, T>(
|
||||||
|
wit_value: &'a InterfaceValue,
|
||||||
|
instruction: Instruction,
|
||||||
|
) -> InstructionResult<T>
|
||||||
|
where
|
||||||
|
T: NativeType + TryFrom<&'a InterfaceValue, Error = WasmValueNativeCastError>,
|
||||||
|
{
|
||||||
|
T::try_from(wit_value)
|
||||||
|
.map_err(|error| InstructionError::new(instruction, InstructionErrorKind::ToNative(error)))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod tests {
|
pub(crate) mod tests {
|
||||||
@ -131,23 +152,12 @@ pub(crate) mod tests {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
hashmap.insert(
|
|
||||||
"alloc".into(),
|
|
||||||
Export {
|
|
||||||
inputs: vec![InterfaceType::I32],
|
|
||||||
outputs: vec![InterfaceType::I32],
|
|
||||||
function: |arguments: &[InterfaceValue]| {
|
|
||||||
let _size: i32 = (&arguments[0]).try_into().unwrap();
|
|
||||||
|
|
||||||
Ok(vec![InterfaceValue::I32(0)])
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
hashmap
|
hashmap
|
||||||
},
|
},
|
||||||
locals_or_imports: {
|
locals_or_imports: {
|
||||||
let mut hashmap = HashMap::new();
|
let mut hashmap = HashMap::new();
|
||||||
|
// sum
|
||||||
hashmap.insert(
|
hashmap.insert(
|
||||||
42,
|
42,
|
||||||
LocalImport {
|
LocalImport {
|
||||||
@ -161,6 +171,19 @@ pub(crate) mod tests {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
// string allocator
|
||||||
|
hashmap.insert(
|
||||||
|
43,
|
||||||
|
LocalImport {
|
||||||
|
inputs: vec![InterfaceType::I32],
|
||||||
|
outputs: vec![InterfaceType::I32],
|
||||||
|
function: |arguments: &[InterfaceValue]| {
|
||||||
|
let _size: i32 = (&arguments[0]).try_into().unwrap();
|
||||||
|
|
||||||
|
Ok(vec![InterfaceValue::I32(0)])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
hashmap
|
hashmap
|
||||||
},
|
},
|
||||||
|
@ -1,131 +0,0 @@
|
|||||||
use crate::interpreter::wasm::values::InterfaceValue;
|
|
||||||
use std::{cell::Cell, convert::TryFrom};
|
|
||||||
|
|
||||||
executable_instruction!(
|
|
||||||
read_utf8(instruction_name: String) -> _ {
|
|
||||||
move |runtime| -> _ {
|
|
||||||
match runtime.stack.pop(2) {
|
|
||||||
Some(inputs) => match runtime.wasm_instance.memory(0) {
|
|
||||||
Some(memory) => {
|
|
||||||
let length = i32::try_from(&inputs[0])? as usize;
|
|
||||||
let pointer = i32::try_from(&inputs[1])? as usize;
|
|
||||||
let memory_view = memory.view();
|
|
||||||
|
|
||||||
if memory_view.len() < pointer + length {
|
|
||||||
return Err(format!(
|
|
||||||
"`{}` failed because it has to read out of the memory bounds (index {} > memory length {}).",
|
|
||||||
instruction_name,
|
|
||||||
pointer + length,
|
|
||||||
memory_view.len()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let data: Vec<u8> = (&memory_view[pointer..pointer + length])
|
|
||||||
.iter()
|
|
||||||
.map(Cell::get)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
match String::from_utf8(data) {
|
|
||||||
Ok(string) => {
|
|
||||||
runtime.stack.push(InterfaceValue::String(string));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Err(utf8_error) => Err(format!(
|
|
||||||
"`{}` failed because the read string isn't UTF-8 valid ({}).",
|
|
||||||
instruction_name,
|
|
||||||
utf8_error,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => Err(format!(
|
|
||||||
"`{}` failed because there is no memory to read.",
|
|
||||||
instruction_name
|
|
||||||
))
|
|
||||||
}
|
|
||||||
None => Err(format!(
|
|
||||||
"`{}` failed because there is not enough data on the stack (needs 2).",
|
|
||||||
instruction_name,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
test_executable_instruction!(
|
|
||||||
test_read_utf8 =
|
|
||||||
instructions: [
|
|
||||||
Instruction::ArgumentGet { index: 1 },
|
|
||||||
Instruction::ArgumentGet { index: 0 },
|
|
||||||
Instruction::ReadUtf8,
|
|
||||||
],
|
|
||||||
invocation_inputs: [
|
|
||||||
InterfaceValue::I32(13),
|
|
||||||
// ^^^^^^^ length
|
|
||||||
InterfaceValue::I32(0),
|
|
||||||
// ^^^^^^ pointer
|
|
||||||
],
|
|
||||||
instance: Instance {
|
|
||||||
memory: Memory::new("Hello, World!".as_bytes().iter().map(|u| Cell::new(*u)).collect()),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
stack: [InterfaceValue::String("Hello, World!".into())],
|
|
||||||
);
|
|
||||||
|
|
||||||
test_executable_instruction!(
|
|
||||||
test_read_utf8__read_out_of_memory =
|
|
||||||
instructions: [
|
|
||||||
Instruction::ArgumentGet { index: 1 },
|
|
||||||
Instruction::ArgumentGet { index: 0 },
|
|
||||||
Instruction::ReadUtf8,
|
|
||||||
],
|
|
||||||
invocation_inputs: [
|
|
||||||
InterfaceValue::I32(13),
|
|
||||||
// ^^^^^^^ length is too long
|
|
||||||
InterfaceValue::I32(0),
|
|
||||||
// ^^^^^^ pointer
|
|
||||||
],
|
|
||||||
instance: Instance {
|
|
||||||
memory: Memory::new("Hello!".as_bytes().iter().map(|u| Cell::new(*u)).collect()),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
error: r#"`read-utf8` failed because it has to read out of the memory bounds (index 13 > memory length 6)."#,
|
|
||||||
);
|
|
||||||
|
|
||||||
test_executable_instruction!(
|
|
||||||
test_read_utf8__invalid_encoding =
|
|
||||||
instructions: [
|
|
||||||
Instruction::ArgumentGet { index: 1 },
|
|
||||||
Instruction::ArgumentGet { index: 0 },
|
|
||||||
Instruction::ReadUtf8,
|
|
||||||
],
|
|
||||||
invocation_inputs: [
|
|
||||||
InterfaceValue::I32(4),
|
|
||||||
// ^^^^^^ length is too long
|
|
||||||
InterfaceValue::I32(0),
|
|
||||||
// ^^^^^^ pointer
|
|
||||||
],
|
|
||||||
instance: Instance {
|
|
||||||
memory: Memory::new(vec![0, 159, 146, 150].iter().map(|b| Cell::new(*b)).collect::<Vec<Cell<u8>>>()),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
error: r#"`read-utf8` failed because the read string isn't UTF-8 valid (invalid utf-8 sequence of 1 bytes from index 1)."#,
|
|
||||||
);
|
|
||||||
|
|
||||||
test_executable_instruction!(
|
|
||||||
test_read_utf8__stack_is_too_small =
|
|
||||||
instructions: [
|
|
||||||
Instruction::ArgumentGet { index: 0 },
|
|
||||||
Instruction::ReadUtf8,
|
|
||||||
// ^^^^^^^^ `read-utf8` expects 2 values on the stack, only one is present.
|
|
||||||
],
|
|
||||||
invocation_inputs: [
|
|
||||||
InterfaceValue::I32(13),
|
|
||||||
InterfaceValue::I32(0),
|
|
||||||
],
|
|
||||||
instance: Instance::new(),
|
|
||||||
error: r#"`read-utf8` failed because there is not enough data on the stack (needs 2)."#,
|
|
||||||
);
|
|
||||||
}
|
|
@ -0,0 +1,175 @@
|
|||||||
|
use super::to_native;
|
||||||
|
use crate::{
|
||||||
|
ast::InterfaceType,
|
||||||
|
errors::{InstructionError, InstructionErrorKind},
|
||||||
|
interpreter::{
|
||||||
|
wasm::{
|
||||||
|
structures::{FunctionIndex, TypedIndex},
|
||||||
|
values::InterfaceValue,
|
||||||
|
},
|
||||||
|
Instruction,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
executable_instruction!(
|
||||||
|
string_to_memory(allocator_index: u32, instruction: Instruction) -> _ {
|
||||||
|
move |runtime| -> _ {
|
||||||
|
let instance = &mut runtime.wasm_instance;
|
||||||
|
let index = FunctionIndex::new(allocator_index as usize);
|
||||||
|
|
||||||
|
let allocator = instance.local_or_import(index).ok_or_else(|| {
|
||||||
|
InstructionError::new(
|
||||||
|
instruction,
|
||||||
|
InstructionErrorKind::LocalOrImportIsMissing { function_index: allocator_index },
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if allocator.inputs() != [InterfaceType::I32] || allocator.outputs() != [InterfaceType::I32] {
|
||||||
|
return Err(InstructionError::new(
|
||||||
|
instruction,
|
||||||
|
InstructionErrorKind::LocalOrImportSignatureMismatch {
|
||||||
|
function_index: allocator_index,
|
||||||
|
expected: (vec![InterfaceType::I32], vec![InterfaceType::I32]),
|
||||||
|
received: (allocator.inputs().to_vec(), allocator.outputs().to_vec())
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
let string = runtime.stack.pop1().ok_or_else(|| {
|
||||||
|
InstructionError::new(
|
||||||
|
instruction,
|
||||||
|
InstructionErrorKind::StackIsTooSmall { needed: 1 }
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let string: String = to_native(&string, instruction)?;
|
||||||
|
let string_bytes = string.as_bytes();
|
||||||
|
let string_length = string_bytes.len() as i32;
|
||||||
|
|
||||||
|
let outputs = allocator.call(&[InterfaceValue::I32(string_length)]).map_err(|_| {
|
||||||
|
InstructionError::new(
|
||||||
|
instruction,
|
||||||
|
InstructionErrorKind::LocalOrImportCall { function_index: allocator_index },
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let string_pointer: i32 = to_native(&outputs[0], instruction)?;
|
||||||
|
|
||||||
|
let memory_index: u32 = 0;
|
||||||
|
let memory_view = instance
|
||||||
|
.memory(memory_index as usize)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
InstructionError::new(
|
||||||
|
instruction,
|
||||||
|
InstructionErrorKind::MemoryIsMissing { memory_index }
|
||||||
|
)
|
||||||
|
})?
|
||||||
|
.view();
|
||||||
|
|
||||||
|
for (nth, byte) in string_bytes.iter().enumerate() {
|
||||||
|
memory_view[string_pointer as usize + nth].set(*byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.stack.push(InterfaceValue::I32(string_pointer));
|
||||||
|
runtime.stack.push(InterfaceValue::I32(string_length));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_string_to_memory =
|
||||||
|
instructions: [
|
||||||
|
Instruction::ArgumentGet { index: 0 },
|
||||||
|
Instruction::StringToMemory { allocator_index: 43 },
|
||||||
|
],
|
||||||
|
invocation_inputs: [InterfaceValue::String("Hello, World!".into())],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [
|
||||||
|
InterfaceValue::I32(0),
|
||||||
|
// ^^^^^^ pointer
|
||||||
|
InterfaceValue::I32(13),
|
||||||
|
// ^^^^^^^ length
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_string_to_memory__roundtrip_with_memory_to_string =
|
||||||
|
instructions: [
|
||||||
|
Instruction::ArgumentGet { index: 0 },
|
||||||
|
Instruction::StringToMemory { allocator_index: 43 },
|
||||||
|
Instruction::MemoryToString,
|
||||||
|
],
|
||||||
|
invocation_inputs: [InterfaceValue::String("Hello, World!".into())],
|
||||||
|
instance: Instance::new(),
|
||||||
|
stack: [InterfaceValue::String("Hello, World!".into())],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_string_to_memory__allocator_does_not_exist =
|
||||||
|
instructions: [Instruction::StringToMemory { allocator_index: 43 }],
|
||||||
|
invocation_inputs: [],
|
||||||
|
instance: Instance { ..Default::default() },
|
||||||
|
error: r#"`string-to-memory 43` the local or import function `43` doesn't exist"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_string_to_memory__stack_is_too_small =
|
||||||
|
instructions: [
|
||||||
|
Instruction::StringToMemory { allocator_index: 43 }
|
||||||
|
// ^^ `43` expects 1 value on the stack, none is present
|
||||||
|
],
|
||||||
|
invocation_inputs: [InterfaceValue::String("Hello, World!".into())],
|
||||||
|
instance: Instance::new(),
|
||||||
|
error: r#"`string-to-memory 43` needed to read `1` value(s) from the stack, but it doesn't contain enough data"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_string_to_memory__failure_when_calling_the_allocator =
|
||||||
|
instructions: [
|
||||||
|
Instruction::ArgumentGet { index: 0 },
|
||||||
|
Instruction::StringToMemory { allocator_index: 153 }
|
||||||
|
],
|
||||||
|
invocation_inputs: [InterfaceValue::String("Hello, World!".into())],
|
||||||
|
instance: {
|
||||||
|
let mut instance = Instance::new();
|
||||||
|
instance.locals_or_imports.insert(
|
||||||
|
153,
|
||||||
|
LocalImport {
|
||||||
|
inputs: vec![InterfaceType::I32],
|
||||||
|
outputs: vec![InterfaceType::I32],
|
||||||
|
function: |_| Err(()),
|
||||||
|
// ^^^^^^^ function fails
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
instance
|
||||||
|
},
|
||||||
|
error: r#"`string-to-memory 153` failed while calling the local or import function `153`"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
test_executable_instruction!(
|
||||||
|
test_string_to_memory__invalid_allocator_signature =
|
||||||
|
instructions: [
|
||||||
|
Instruction::ArgumentGet { index: 0 },
|
||||||
|
Instruction::StringToMemory { allocator_index: 153 }
|
||||||
|
],
|
||||||
|
invocation_inputs: [InterfaceValue::String("Hello, World!".into())],
|
||||||
|
instance: {
|
||||||
|
let mut instance = Instance::new();
|
||||||
|
instance.locals_or_imports.insert(
|
||||||
|
153,
|
||||||
|
LocalImport {
|
||||||
|
inputs: vec![InterfaceType::I32, InterfaceType::I32],
|
||||||
|
outputs: vec![],
|
||||||
|
function: |_| Err(()),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
instance
|
||||||
|
},
|
||||||
|
error: r#"`string-to-memory 153` the local or import function `153` has the signature `[I32] -> [I32]` but it received values of kind `[I32, I32] -> []`"#,
|
||||||
|
);
|
||||||
|
}
|
@ -1,169 +0,0 @@
|
|||||||
use crate::interpreter::wasm::values::{InterfaceType, InterfaceValue};
|
|
||||||
use std::convert::TryInto;
|
|
||||||
|
|
||||||
executable_instruction!(
|
|
||||||
write_utf8(allocator_name: String, instruction_name: String) -> _ {
|
|
||||||
move |runtime| -> _ {
|
|
||||||
let instance = &mut runtime.wasm_instance;
|
|
||||||
|
|
||||||
match instance.export(&allocator_name) {
|
|
||||||
Some(allocator) => {
|
|
||||||
if allocator.inputs() != [InterfaceType::I32] ||
|
|
||||||
allocator.outputs() != [InterfaceType::I32] {
|
|
||||||
return Err(format!(
|
|
||||||
"`{}` failed because the allocator `{}` has an invalid signature (expects [I32] -> [I32]).",
|
|
||||||
instruction_name,
|
|
||||||
allocator_name,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
match instance.memory(0) {
|
|
||||||
Some(memory) => match runtime.stack.pop1() {
|
|
||||||
Some(string) => {
|
|
||||||
let memory_view = memory.view();
|
|
||||||
|
|
||||||
let string: String = (&string).try_into()?;
|
|
||||||
let string_bytes = string.as_bytes();
|
|
||||||
let string_length = (string_bytes.len() as i32)
|
|
||||||
.try_into()
|
|
||||||
.map_err(|error| format!("{}", error))?;
|
|
||||||
|
|
||||||
match allocator.call(&[InterfaceValue::I32(string_length)]) {
|
|
||||||
Ok(outputs) => {
|
|
||||||
let string_pointer: i32 = (&outputs[0]).try_into()?;
|
|
||||||
|
|
||||||
for (nth, byte) in string_bytes.iter().enumerate() {
|
|
||||||
memory_view[string_pointer as usize + nth].set(*byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
runtime.stack.push(InterfaceValue::I32(string_pointer));
|
|
||||||
runtime.stack.push(InterfaceValue::I32(string_length));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Err(_) => Err(format!(
|
|
||||||
"`{}` failed when calling the allocator `{}`.",
|
|
||||||
instruction_name,
|
|
||||||
allocator_name,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => Err(format!(
|
|
||||||
"`{}` cannot call the allocator `{}` because there is not enough data on the stack for the arguments (needs {}).",
|
|
||||||
instruction_name,
|
|
||||||
allocator_name,
|
|
||||||
1
|
|
||||||
))
|
|
||||||
}
|
|
||||||
None => Err(format!(
|
|
||||||
"`{}` failed because there is no memory to write into.",
|
|
||||||
instruction_name
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => Err(format!(
|
|
||||||
"`{}` failed because the exported function `{}` (the allocator) doesn't exist.",
|
|
||||||
instruction_name,
|
|
||||||
allocator_name
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
test_executable_instruction!(
|
|
||||||
test_write_utf8 =
|
|
||||||
instructions: [
|
|
||||||
Instruction::ArgumentGet { index: 0 },
|
|
||||||
Instruction::WriteUtf8 { allocator_name: "alloc" },
|
|
||||||
],
|
|
||||||
invocation_inputs: [InterfaceValue::String("Hello, World!".into())],
|
|
||||||
instance: Instance::new(),
|
|
||||||
stack: [
|
|
||||||
InterfaceValue::I32(0),
|
|
||||||
// ^^^^^^ pointer
|
|
||||||
InterfaceValue::I32(13),
|
|
||||||
// ^^^^^^^ length
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
test_executable_instruction!(
|
|
||||||
test_write_utf8__roundtrip_with_read_utf8 =
|
|
||||||
instructions: [
|
|
||||||
Instruction::ArgumentGet { index: 0 },
|
|
||||||
Instruction::WriteUtf8 { allocator_name: "alloc" },
|
|
||||||
Instruction::ReadUtf8,
|
|
||||||
],
|
|
||||||
invocation_inputs: [InterfaceValue::String("Hello, World!".into())],
|
|
||||||
instance: Instance::new(),
|
|
||||||
stack: [InterfaceValue::String("Hello, World!".into())],
|
|
||||||
);
|
|
||||||
|
|
||||||
test_executable_instruction!(
|
|
||||||
test_write_utf8__allocator_does_not_exist =
|
|
||||||
instructions: [Instruction::WriteUtf8 { allocator_name: "alloc" }],
|
|
||||||
invocation_inputs: [],
|
|
||||||
instance: Instance { ..Default::default() },
|
|
||||||
error: r#"`write-utf8 "alloc"` failed because the exported function `alloc` (the allocator) doesn't exist."#,
|
|
||||||
);
|
|
||||||
|
|
||||||
test_executable_instruction!(
|
|
||||||
test_write_utf8__stack_is_too_small =
|
|
||||||
instructions: [
|
|
||||||
Instruction::WriteUtf8 { allocator_name: "alloc" }
|
|
||||||
// ^^^^^ `alloc` expects 1 value on the stack, none is present
|
|
||||||
],
|
|
||||||
invocation_inputs: [InterfaceValue::String("Hello, World!".into())],
|
|
||||||
instance: Instance::new(),
|
|
||||||
error: r#"`write-utf8 "alloc"` cannot call the allocator `alloc` because there is not enough data on the stack for the arguments (needs 1)."#,
|
|
||||||
);
|
|
||||||
|
|
||||||
test_executable_instruction!(
|
|
||||||
test_write_utf8__failure_when_calling_the_allocator =
|
|
||||||
instructions: [
|
|
||||||
Instruction::ArgumentGet { index: 0 },
|
|
||||||
Instruction::WriteUtf8 { allocator_name: "alloc-fail" }
|
|
||||||
],
|
|
||||||
invocation_inputs: [InterfaceValue::String("Hello, World!".into())],
|
|
||||||
instance: {
|
|
||||||
let mut instance = Instance::new();
|
|
||||||
instance.exports.insert(
|
|
||||||
"alloc-fail".into(),
|
|
||||||
Export {
|
|
||||||
inputs: vec![InterfaceType::I32],
|
|
||||||
outputs: vec![InterfaceType::I32],
|
|
||||||
function: |_| Err(()),
|
|
||||||
// ^^^^^^^ function fails
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
instance
|
|
||||||
},
|
|
||||||
error: r#"`write-utf8 "alloc-fail"` failed when calling the allocator `alloc-fail`."#,
|
|
||||||
);
|
|
||||||
|
|
||||||
test_executable_instruction!(
|
|
||||||
test_write_utf8__invalid_allocator_signature =
|
|
||||||
instructions: [
|
|
||||||
Instruction::ArgumentGet { index: 0 },
|
|
||||||
Instruction::WriteUtf8 { allocator_name: "alloc-fail" }
|
|
||||||
],
|
|
||||||
invocation_inputs: [InterfaceValue::String("Hello, World!".into())],
|
|
||||||
instance: {
|
|
||||||
let mut instance = Instance::new();
|
|
||||||
instance.exports.insert(
|
|
||||||
"alloc-fail".into(),
|
|
||||||
Export {
|
|
||||||
inputs: vec![InterfaceType::I32, InterfaceType::I32],
|
|
||||||
outputs: vec![],
|
|
||||||
function: |_| Err(()),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
instance
|
|
||||||
},
|
|
||||||
error: r#"`write-utf8 "alloc-fail"` failed because the allocator `alloc-fail` has an invalid signature (expects [I32] -> [I32])."#,
|
|
||||||
);
|
|
||||||
}
|
|
@ -5,6 +5,7 @@ mod instructions;
|
|||||||
pub mod stack;
|
pub mod stack;
|
||||||
pub mod wasm;
|
pub mod wasm;
|
||||||
|
|
||||||
|
use crate::errors::{InstructionResult, InterpreterResult};
|
||||||
pub use instruction::Instruction;
|
pub use instruction::Instruction;
|
||||||
use stack::Stack;
|
use stack::Stack;
|
||||||
use std::{convert::TryFrom, marker::PhantomData};
|
use std::{convert::TryFrom, marker::PhantomData};
|
||||||
@ -38,7 +39,9 @@ where
|
|||||||
/// Type alias for an executable instruction. It's an implementation
|
/// Type alias for an executable instruction. It's an implementation
|
||||||
/// details, but an instruction is a boxed closure instance.
|
/// details, but an instruction is a boxed closure instance.
|
||||||
pub(crate) type ExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView> = Box<
|
pub(crate) type ExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView> = Box<
|
||||||
dyn Fn(&mut Runtime<Instance, Export, LocalImport, Memory, MemoryView>) -> Result<(), String>,
|
dyn Fn(
|
||||||
|
&mut Runtime<Instance, Export, LocalImport, Memory, MemoryView>,
|
||||||
|
) -> InstructionResult<()>,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
/// An interpreter is the central piece of this crate. It is a set of
|
/// An interpreter is the central piece of this crate. It is a set of
|
||||||
@ -71,7 +74,7 @@ pub(crate) type ExecutableInstruction<Instance, Export, LocalImport, Memory, Mem
|
|||||||
/// let interpreter: Interpreter<Instance, Export, LocalImport, Memory, MemoryView> = (&vec![
|
/// let interpreter: Interpreter<Instance, Export, LocalImport, Memory, MemoryView> = (&vec![
|
||||||
/// Instruction::ArgumentGet { index: 1 },
|
/// Instruction::ArgumentGet { index: 1 },
|
||||||
/// Instruction::ArgumentGet { index: 0 },
|
/// Instruction::ArgumentGet { index: 0 },
|
||||||
/// Instruction::CallExport { export_name: "sum" },
|
/// Instruction::CallCore { function_index: 42 },
|
||||||
/// ])
|
/// ])
|
||||||
/// .try_into()
|
/// .try_into()
|
||||||
/// .unwrap();
|
/// .unwrap();
|
||||||
@ -81,12 +84,12 @@ pub(crate) type ExecutableInstruction<Instance, Export, LocalImport, Memory, Mem
|
|||||||
///
|
///
|
||||||
/// // 3. Creates a WebAssembly instance.
|
/// // 3. Creates a WebAssembly instance.
|
||||||
/// let mut instance = Instance {
|
/// let mut instance = Instance {
|
||||||
/// // 3.1. Defines one exported function: `fn sum(a: i32, b: i32) -> i32 { a + b }`.
|
/// // 3.1. Defines one function: `fn sum(a: i32, b: i32) -> i32 { a + b }`.
|
||||||
/// exports: {
|
/// locals_or_imports: {
|
||||||
/// let mut hashmap = HashMap::new();
|
/// let mut hashmap = HashMap::new();
|
||||||
/// hashmap.insert(
|
/// hashmap.insert(
|
||||||
/// "sum".into(),
|
/// 42,
|
||||||
/// Export {
|
/// LocalImport {
|
||||||
/// // Defines the argument types of the function.
|
/// // Defines the argument types of the function.
|
||||||
/// inputs: vec![InterfaceType::I32, InterfaceType::I32],
|
/// inputs: vec![InterfaceType::I32, InterfaceType::I32],
|
||||||
///
|
///
|
||||||
@ -154,7 +157,7 @@ where
|
|||||||
&self,
|
&self,
|
||||||
invocation_inputs: &[InterfaceValue],
|
invocation_inputs: &[InterfaceValue],
|
||||||
wasm_instance: &mut Instance,
|
wasm_instance: &mut Instance,
|
||||||
) -> Result<Stack<InterfaceValue>, String> {
|
) -> InterpreterResult<Stack<InterfaceValue>> {
|
||||||
let mut runtime = Runtime {
|
let mut runtime = Runtime {
|
||||||
invocation_inputs,
|
invocation_inputs,
|
||||||
stack: Stack::new(),
|
stack: Stack::new(),
|
||||||
@ -163,10 +166,7 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
for executable_instruction in self.iter() {
|
for executable_instruction in self.iter() {
|
||||||
match executable_instruction(&mut runtime) {
|
executable_instruction(&mut runtime)?;
|
||||||
Ok(_) => continue,
|
|
||||||
Err(message) => return Err(message),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(runtime.stack)
|
Ok(runtime.stack)
|
||||||
@ -174,8 +174,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Transforms a `Vec<Instruction>` into an `Interpreter`.
|
/// Transforms a `Vec<Instruction>` into an `Interpreter`.
|
||||||
impl<'binary_input, Instance, Export, LocalImport, Memory, MemoryView>
|
impl<Instance, Export, LocalImport, Memory, MemoryView> TryFrom<&Vec<Instruction>>
|
||||||
TryFrom<&Vec<Instruction<'binary_input>>>
|
|
||||||
for Interpreter<Instance, Export, LocalImport, Memory, MemoryView>
|
for Interpreter<Instance, Export, LocalImport, Memory, MemoryView>
|
||||||
where
|
where
|
||||||
Export: wasm::structures::Export,
|
Export: wasm::structures::Export,
|
||||||
@ -184,28 +183,64 @@ where
|
|||||||
MemoryView: wasm::structures::MemoryView,
|
MemoryView: wasm::structures::MemoryView,
|
||||||
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
|
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
|
||||||
{
|
{
|
||||||
type Error = String;
|
type Error = ();
|
||||||
|
|
||||||
fn try_from(instructions: &Vec<Instruction>) -> Result<Self, Self::Error> {
|
fn try_from(instructions: &Vec<Instruction>) -> Result<Self, Self::Error> {
|
||||||
let executable_instructions = instructions
|
let executable_instructions = instructions
|
||||||
.iter()
|
.iter()
|
||||||
.map(|instruction| {
|
.map(|instruction| {
|
||||||
let instruction_name = instruction.to_string();
|
|
||||||
|
|
||||||
match instruction {
|
match instruction {
|
||||||
Instruction::ArgumentGet { index } => {
|
Instruction::ArgumentGet { index } => {
|
||||||
instructions::argument_get(*index, instruction_name)
|
instructions::argument_get(*index, *instruction)
|
||||||
}
|
}
|
||||||
Instruction::Call { function_index } => {
|
Instruction::CallCore { function_index } => {
|
||||||
instructions::call(*function_index, instruction_name)
|
instructions::call_core(*function_index, *instruction)
|
||||||
}
|
}
|
||||||
Instruction::CallExport { export_name } => {
|
Instruction::MemoryToString => instructions::memory_to_string(*instruction),
|
||||||
instructions::call_export((*export_name).to_owned(), instruction_name)
|
Instruction::StringToMemory { allocator_index } => {
|
||||||
|
instructions::string_to_memory(*allocator_index, *instruction)
|
||||||
}
|
}
|
||||||
Instruction::ReadUtf8 => instructions::read_utf8(instruction_name),
|
|
||||||
Instruction::WriteUtf8 { allocator_name } => {
|
Instruction::I32ToS8 => instructions::i32_to_s8(*instruction),
|
||||||
instructions::write_utf8((*allocator_name).to_owned(), instruction_name)
|
//Instruction::I32ToS8X
|
||||||
|
Instruction::I32ToU8 => instructions::i32_to_u8(*instruction),
|
||||||
|
Instruction::I32ToS16 => instructions::i32_to_s16(*instruction),
|
||||||
|
//Instruction::I32ToS16X
|
||||||
|
Instruction::I32ToU16 => instructions::i32_to_u16(*instruction),
|
||||||
|
Instruction::I32ToS32 => instructions::i32_to_s32(*instruction),
|
||||||
|
Instruction::I32ToU32 => instructions::i32_to_u32(*instruction),
|
||||||
|
Instruction::I32ToS64 => instructions::i32_to_s64(*instruction),
|
||||||
|
Instruction::I32ToU64 => instructions::i32_to_u64(*instruction),
|
||||||
|
Instruction::I64ToS8 => instructions::i64_to_s8(*instruction),
|
||||||
|
//Instruction::I64ToS8X
|
||||||
|
Instruction::I64ToU8 => instructions::i64_to_u8(*instruction),
|
||||||
|
Instruction::I64ToS16 => instructions::i64_to_s16(*instruction),
|
||||||
|
//Instruction::I64ToS16X
|
||||||
|
Instruction::I64ToU16 => instructions::i64_to_u16(*instruction),
|
||||||
|
Instruction::I64ToS32 => instructions::i64_to_s32(*instruction),
|
||||||
|
Instruction::I64ToU32 => instructions::i64_to_u32(*instruction),
|
||||||
|
Instruction::I64ToS64 => instructions::i64_to_s64(*instruction),
|
||||||
|
Instruction::I64ToU64 => instructions::i64_to_u64(*instruction),
|
||||||
|
Instruction::S8ToI32 => instructions::s8_to_i32(*instruction),
|
||||||
|
Instruction::U8ToI32 => instructions::u8_to_i32(*instruction),
|
||||||
|
Instruction::S16ToI32 => instructions::s16_to_i32(*instruction),
|
||||||
|
Instruction::U16ToI32 => instructions::u16_to_i32(*instruction),
|
||||||
|
Instruction::S32ToI32 => instructions::s32_to_i32(*instruction),
|
||||||
|
Instruction::U32ToI32 => instructions::u32_to_i32(*instruction),
|
||||||
|
Instruction::S64ToI32 | Instruction::S64ToI32X => {
|
||||||
|
instructions::s64_to_i32(*instruction)
|
||||||
}
|
}
|
||||||
|
Instruction::U64ToI32 | Instruction::U64ToI32X => {
|
||||||
|
instructions::u64_to_i32(*instruction)
|
||||||
|
}
|
||||||
|
Instruction::S8ToI64 => instructions::s8_to_i64(*instruction),
|
||||||
|
Instruction::U8ToI64 => instructions::u8_to_i64(*instruction),
|
||||||
|
Instruction::S16ToI64 => instructions::s16_to_i64(*instruction),
|
||||||
|
Instruction::U16ToI64 => instructions::u16_to_i64(*instruction),
|
||||||
|
Instruction::S32ToI64 => instructions::s32_to_i64(*instruction),
|
||||||
|
Instruction::U32ToI64 => instructions::u32_to_i64(*instruction),
|
||||||
|
Instruction::S64ToI64 => instructions::s64_to_i64(*instruction),
|
||||||
|
Instruction::U64ToI64 => instructions::u64_to_i64(*instruction),
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -216,24 +251,3 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::{wasm::structures::EmptyMemoryView, Instruction, Interpreter};
|
|
||||||
use std::convert::TryInto;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_interpreter_from_instructions() {
|
|
||||||
let instructions = vec![
|
|
||||||
Instruction::ArgumentGet { index: 0 },
|
|
||||||
Instruction::ArgumentGet { index: 0 },
|
|
||||||
Instruction::CallExport { export_name: "foo" },
|
|
||||||
Instruction::ReadUtf8,
|
|
||||||
Instruction::Call { function_index: 7 },
|
|
||||||
];
|
|
||||||
let interpreter: Interpreter<(), (), (), (), EmptyMemoryView> =
|
|
||||||
(&instructions).try_into().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(interpreter.executable_instructions.len(), 5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
use std::convert::TryFrom;
|
|
||||||
|
|
||||||
pub use crate::ast::InterfaceType;
|
pub use crate::ast::InterfaceType;
|
||||||
|
use crate::errors::WasmValueNativeCastError;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum InterfaceValue {
|
pub enum InterfaceValue {
|
||||||
@ -49,35 +49,46 @@ impl Default for InterfaceValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! from_x_for_interface_value {
|
pub trait NativeType {
|
||||||
($native_type:ty, $value_variant:ident) => {
|
const INTERFACE_TYPE: InterfaceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! native {
|
||||||
|
($native_type:ty, $variant:ident) => {
|
||||||
|
impl NativeType for $native_type {
|
||||||
|
const INTERFACE_TYPE: InterfaceType = InterfaceType::$variant;
|
||||||
|
}
|
||||||
|
|
||||||
impl From<$native_type> for InterfaceValue {
|
impl From<$native_type> for InterfaceValue {
|
||||||
fn from(n: $native_type) -> Self {
|
fn from(n: $native_type) -> Self {
|
||||||
Self::$value_variant(n)
|
Self::$variant(n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&InterfaceValue> for $native_type {
|
impl TryFrom<&InterfaceValue> for $native_type {
|
||||||
type Error = &'static str;
|
type Error = WasmValueNativeCastError;
|
||||||
|
|
||||||
fn try_from(w: &InterfaceValue) -> Result<Self, Self::Error> {
|
fn try_from(w: &InterfaceValue) -> Result<Self, Self::Error> {
|
||||||
match w {
|
match w {
|
||||||
InterfaceValue::$value_variant(n) => Ok(n.clone()),
|
InterfaceValue::$variant(n) => Ok(n.clone()),
|
||||||
_ => Err("Invalid cast."),
|
_ => Err(WasmValueNativeCastError {
|
||||||
|
from: w.into(),
|
||||||
|
to: <$native_type>::INTERFACE_TYPE,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
from_x_for_interface_value!(i8, S8);
|
native!(i8, S8);
|
||||||
from_x_for_interface_value!(i16, S16);
|
native!(i16, S16);
|
||||||
from_x_for_interface_value!(u8, U8);
|
native!(u8, U8);
|
||||||
from_x_for_interface_value!(u16, U16);
|
native!(u16, U16);
|
||||||
from_x_for_interface_value!(u32, U32);
|
native!(u32, U32);
|
||||||
from_x_for_interface_value!(u64, U64);
|
native!(u64, U64);
|
||||||
from_x_for_interface_value!(f32, F32);
|
native!(f32, F32);
|
||||||
from_x_for_interface_value!(f64, F64);
|
native!(f64, F64);
|
||||||
from_x_for_interface_value!(String, String);
|
native!(String, String);
|
||||||
from_x_for_interface_value!(i32, I32);
|
native!(i32, I32);
|
||||||
from_x_for_interface_value!(i64, I64);
|
native!(i64, I64);
|
||||||
|
@ -55,4 +55,5 @@ pub mod ast;
|
|||||||
mod macros;
|
mod macros;
|
||||||
pub mod decoders;
|
pub mod decoders;
|
||||||
pub mod encoders;
|
pub mod encoders;
|
||||||
|
pub mod errors;
|
||||||
pub mod interpreter;
|
pub mod interpreter;
|
||||||
|
@ -40,18 +40,19 @@ macro_rules! consume {
|
|||||||
/// Check the existing executable instruction to get more examples.
|
/// Check the existing executable instruction to get more examples.
|
||||||
macro_rules! executable_instruction {
|
macro_rules! executable_instruction {
|
||||||
($name:ident ( $($argument_name:ident: $argument_type:ty),* ) -> _ $implementation:block ) => {
|
($name:ident ( $($argument_name:ident: $argument_type:ty),* ) -> _ $implementation:block ) => {
|
||||||
use crate::interpreter::{ExecutableInstruction, wasm, stack::Stackable};
|
|
||||||
|
|
||||||
pub(crate) fn $name<Instance, Export, LocalImport, Memory, MemoryView>(
|
pub(crate) fn $name<Instance, Export, LocalImport, Memory, MemoryView>(
|
||||||
$($argument_name: $argument_type),*
|
$($argument_name: $argument_type),*
|
||||||
) -> ExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView>
|
) -> crate::interpreter::ExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView>
|
||||||
where
|
where
|
||||||
Export: wasm::structures::Export,
|
Export: crate::interpreter::wasm::structures::Export,
|
||||||
LocalImport: wasm::structures::LocalImport,
|
LocalImport: crate::interpreter::wasm::structures::LocalImport,
|
||||||
Memory: wasm::structures::Memory<MemoryView>,
|
Memory: crate::interpreter::wasm::structures::Memory<MemoryView>,
|
||||||
MemoryView: wasm::structures::MemoryView,
|
MemoryView: crate::interpreter::wasm::structures::MemoryView,
|
||||||
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
|
Instance: crate::interpreter::wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
|
||||||
{
|
{
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use crate::interpreter::{stack::Stackable};
|
||||||
|
|
||||||
Box::new($implementation)
|
Box::new($implementation)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -121,7 +122,7 @@ macro_rules! test_executable_instruction {
|
|||||||
|
|
||||||
assert!(run.is_err());
|
assert!(run.is_err());
|
||||||
|
|
||||||
let error = run.unwrap_err();
|
let error = run.unwrap_err().to_string();
|
||||||
|
|
||||||
assert_eq!(error, String::from($error));
|
assert_eq!(error, String::from($error));
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,8 @@ edition = "2018"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wabt = "0.9.1"
|
wabt = "0.9.1"
|
||||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.14.1" }
|
wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" }
|
||||||
wasmer-runtime = { path = "../runtime", version = "0.14.1" }
|
wasmer-runtime = { path = "../runtime", version = "0.16.2" }
|
||||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.14.1", features = ["test"] }
|
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.2", features = ["test"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wasmer-llvm-backend"
|
name = "wasmer-llvm-backend"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||||
repository = "https://github.com/wasmerio/wasmer"
|
repository = "https://github.com/wasmerio/wasmer"
|
||||||
@ -10,7 +10,7 @@ edition = "2018"
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.14.1", features = ["generate-debug-information-no-export-symbols"] }
|
wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2", features = ["generate-debug-information-no-export-symbols"] }
|
||||||
wasmparser = "0.51.3"
|
wasmparser = "0.51.3"
|
||||||
smallvec = "0.6"
|
smallvec = "0.6"
|
||||||
goblin = "0.0.24"
|
goblin = "0.0.24"
|
||||||
@ -19,13 +19,13 @@ byteorder = "1"
|
|||||||
|
|
||||||
[target.'cfg(target_arch = "x86_64")'.dependencies.inkwell]
|
[target.'cfg(target_arch = "x86_64")'.dependencies.inkwell]
|
||||||
git = "https://github.com/TheDan64/inkwell"
|
git = "https://github.com/TheDan64/inkwell"
|
||||||
rev = "0a864ebf68b33d4d514b67796264b03898aa0944"
|
rev = "af4cf4efbb27cdea8a54175ffc18ffd91964618c"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["llvm8-0", "target-x86"]
|
features = ["llvm8-0", "target-x86"]
|
||||||
|
|
||||||
[target.'cfg(target_arch = "aarch64")'.dependencies.inkwell]
|
[target.'cfg(target_arch = "aarch64")'.dependencies.inkwell]
|
||||||
git = "https://github.com/TheDan64/inkwell"
|
git = "https://github.com/TheDan64/inkwell"
|
||||||
rev = "0a864ebf68b33d4d514b67796264b03898aa0944"
|
rev = "af4cf4efbb27cdea8a54175ffc18ffd91964618c"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["llvm8-0", "target-aarch64"]
|
features = ["llvm8-0", "target-aarch64"]
|
||||||
|
|
||||||
|
@ -354,15 +354,15 @@ fn trap_if_not_representable_as_int<'ctx>(
|
|||||||
let failure_block = context.append_basic_block(*function, "conversion_failure_block");
|
let failure_block = context.append_basic_block(*function, "conversion_failure_block");
|
||||||
let continue_block = context.append_basic_block(*function, "conversion_success_block");
|
let continue_block = context.append_basic_block(*function, "conversion_success_block");
|
||||||
|
|
||||||
builder.build_conditional_branch(out_of_bounds, &failure_block, &continue_block);
|
builder.build_conditional_branch(out_of_bounds, failure_block, continue_block);
|
||||||
builder.position_at_end(&failure_block);
|
builder.position_at_end(failure_block);
|
||||||
builder.build_call(
|
builder.build_call(
|
||||||
intrinsics.throw_trap,
|
intrinsics.throw_trap,
|
||||||
&[intrinsics.trap_illegal_arithmetic],
|
&[intrinsics.trap_illegal_arithmetic],
|
||||||
"throw",
|
"throw",
|
||||||
);
|
);
|
||||||
builder.build_unreachable();
|
builder.build_unreachable();
|
||||||
builder.position_at_end(&continue_block);
|
builder.position_at_end(continue_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trap_if_zero_or_overflow<'ctx>(
|
fn trap_if_zero_or_overflow<'ctx>(
|
||||||
@ -418,15 +418,15 @@ fn trap_if_zero_or_overflow<'ctx>(
|
|||||||
|
|
||||||
let shouldnt_trap_block = context.append_basic_block(*function, "shouldnt_trap_block");
|
let shouldnt_trap_block = context.append_basic_block(*function, "shouldnt_trap_block");
|
||||||
let should_trap_block = context.append_basic_block(*function, "should_trap_block");
|
let should_trap_block = context.append_basic_block(*function, "should_trap_block");
|
||||||
builder.build_conditional_branch(should_trap, &should_trap_block, &shouldnt_trap_block);
|
builder.build_conditional_branch(should_trap, should_trap_block, shouldnt_trap_block);
|
||||||
builder.position_at_end(&should_trap_block);
|
builder.position_at_end(should_trap_block);
|
||||||
builder.build_call(
|
builder.build_call(
|
||||||
intrinsics.throw_trap,
|
intrinsics.throw_trap,
|
||||||
&[intrinsics.trap_illegal_arithmetic],
|
&[intrinsics.trap_illegal_arithmetic],
|
||||||
"throw",
|
"throw",
|
||||||
);
|
);
|
||||||
builder.build_unreachable();
|
builder.build_unreachable();
|
||||||
builder.position_at_end(&shouldnt_trap_block);
|
builder.position_at_end(shouldnt_trap_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trap_if_zero<'ctx>(
|
fn trap_if_zero<'ctx>(
|
||||||
@ -460,15 +460,15 @@ fn trap_if_zero<'ctx>(
|
|||||||
|
|
||||||
let shouldnt_trap_block = context.append_basic_block(*function, "shouldnt_trap_block");
|
let shouldnt_trap_block = context.append_basic_block(*function, "shouldnt_trap_block");
|
||||||
let should_trap_block = context.append_basic_block(*function, "should_trap_block");
|
let should_trap_block = context.append_basic_block(*function, "should_trap_block");
|
||||||
builder.build_conditional_branch(should_trap, &should_trap_block, &shouldnt_trap_block);
|
builder.build_conditional_branch(should_trap, should_trap_block, shouldnt_trap_block);
|
||||||
builder.position_at_end(&should_trap_block);
|
builder.position_at_end(should_trap_block);
|
||||||
builder.build_call(
|
builder.build_call(
|
||||||
intrinsics.throw_trap,
|
intrinsics.throw_trap,
|
||||||
&[intrinsics.trap_illegal_arithmetic],
|
&[intrinsics.trap_illegal_arithmetic],
|
||||||
"throw",
|
"throw",
|
||||||
);
|
);
|
||||||
builder.build_unreachable();
|
builder.build_unreachable();
|
||||||
builder.position_at_end(&shouldnt_trap_block);
|
builder.position_at_end(shouldnt_trap_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn v128_into_int_vec<'ctx>(
|
fn v128_into_int_vec<'ctx>(
|
||||||
@ -774,17 +774,17 @@ fn resolve_memory_ptr<'ctx>(
|
|||||||
let not_in_bounds_block = context.append_basic_block(*function, "not_in_bounds_block");
|
let not_in_bounds_block = context.append_basic_block(*function, "not_in_bounds_block");
|
||||||
builder.build_conditional_branch(
|
builder.build_conditional_branch(
|
||||||
ptr_in_bounds,
|
ptr_in_bounds,
|
||||||
&in_bounds_continue_block,
|
in_bounds_continue_block,
|
||||||
¬_in_bounds_block,
|
not_in_bounds_block,
|
||||||
);
|
);
|
||||||
builder.position_at_end(¬_in_bounds_block);
|
builder.position_at_end(not_in_bounds_block);
|
||||||
builder.build_call(
|
builder.build_call(
|
||||||
intrinsics.throw_trap,
|
intrinsics.throw_trap,
|
||||||
&[intrinsics.trap_memory_oob],
|
&[intrinsics.trap_memory_oob],
|
||||||
"throw",
|
"throw",
|
||||||
);
|
);
|
||||||
builder.build_unreachable();
|
builder.build_unreachable();
|
||||||
builder.position_at_end(&in_bounds_continue_block);
|
builder.position_at_end(in_bounds_continue_block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -918,9 +918,9 @@ fn trap_if_misaligned<'ctx>(
|
|||||||
|
|
||||||
let continue_block = context.append_basic_block(*function, "aligned_access_continue_block");
|
let continue_block = context.append_basic_block(*function, "aligned_access_continue_block");
|
||||||
let not_aligned_block = context.append_basic_block(*function, "misaligned_trap_block");
|
let not_aligned_block = context.append_basic_block(*function, "misaligned_trap_block");
|
||||||
builder.build_conditional_branch(aligned, &continue_block, ¬_aligned_block);
|
builder.build_conditional_branch(aligned, continue_block, not_aligned_block);
|
||||||
|
|
||||||
builder.position_at_end(¬_aligned_block);
|
builder.position_at_end(not_aligned_block);
|
||||||
builder.build_call(
|
builder.build_call(
|
||||||
intrinsics.throw_trap,
|
intrinsics.throw_trap,
|
||||||
&[intrinsics.trap_misaligned_atomic],
|
&[intrinsics.trap_misaligned_atomic],
|
||||||
@ -928,7 +928,7 @@ fn trap_if_misaligned<'ctx>(
|
|||||||
);
|
);
|
||||||
builder.build_unreachable();
|
builder.build_unreachable();
|
||||||
|
|
||||||
builder.position_at_end(&continue_block);
|
builder.position_at_end(continue_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -1053,11 +1053,11 @@ impl<'ctx> FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator<'ct
|
|||||||
.builder
|
.builder
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.build_unconditional_branch(&start_of_code_block);
|
.build_unconditional_branch(start_of_code_block);
|
||||||
self.builder
|
self.builder
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.position_at_end(&start_of_code_block);
|
.position_at_end(start_of_code_block);
|
||||||
|
|
||||||
let cache_builder = self.context.as_ref().unwrap().create_builder();
|
let cache_builder = self.context.as_ref().unwrap().create_builder();
|
||||||
cache_builder.position_before(&entry_end_inst);
|
cache_builder.position_before(&entry_end_inst);
|
||||||
@ -1210,7 +1210,7 @@ impl<'ctx> FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator<'ct
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
let end_block = context.append_basic_block(function, "end");
|
let end_block = context.append_basic_block(function, "end");
|
||||||
builder.position_at_end(&end_block);
|
builder.position_at_end(end_block);
|
||||||
|
|
||||||
let phis = if let Ok(wasmer_ty) = blocktype_to_type(ty) {
|
let phis = if let Ok(wasmer_ty) = blocktype_to_type(ty) {
|
||||||
let llvm_ty = type_to_llvm(intrinsics, wasmer_ty);
|
let llvm_ty = type_to_llvm(intrinsics, wasmer_ty);
|
||||||
@ -1223,15 +1223,15 @@ impl<'ctx> FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator<'ct
|
|||||||
};
|
};
|
||||||
|
|
||||||
state.push_block(end_block, phis);
|
state.push_block(end_block, phis);
|
||||||
builder.position_at_end(¤t_block);
|
builder.position_at_end(current_block);
|
||||||
}
|
}
|
||||||
Operator::Loop { ty } => {
|
Operator::Loop { ty } => {
|
||||||
let loop_body = context.append_basic_block(function, "loop_body");
|
let loop_body = context.append_basic_block(function, "loop_body");
|
||||||
let loop_next = context.append_basic_block(function, "loop_outer");
|
let loop_next = context.append_basic_block(function, "loop_outer");
|
||||||
|
|
||||||
builder.build_unconditional_branch(&loop_body);
|
builder.build_unconditional_branch(loop_body);
|
||||||
|
|
||||||
builder.position_at_end(&loop_next);
|
builder.position_at_end(loop_next);
|
||||||
let phis = if let Ok(wasmer_ty) = blocktype_to_type(ty) {
|
let phis = if let Ok(wasmer_ty) = blocktype_to_type(ty) {
|
||||||
let llvm_ty = type_to_llvm(intrinsics, wasmer_ty);
|
let llvm_ty = type_to_llvm(intrinsics, wasmer_ty);
|
||||||
[llvm_ty]
|
[llvm_ty]
|
||||||
@ -1242,7 +1242,7 @@ impl<'ctx> FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator<'ct
|
|||||||
SmallVec::new()
|
SmallVec::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
builder.position_at_end(&loop_body);
|
builder.position_at_end(loop_body);
|
||||||
|
|
||||||
if self.track_state {
|
if self.track_state {
|
||||||
if let Some(offset) = opcode_offset {
|
if let Some(offset) = opcode_offset {
|
||||||
@ -1299,10 +1299,10 @@ impl<'ctx> FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator<'ct
|
|||||||
// pop a value off the value stack and load it into
|
// pop a value off the value stack and load it into
|
||||||
// the corresponding phi.
|
// the corresponding phi.
|
||||||
for (phi, value) in frame.phis().iter().zip(values) {
|
for (phi, value) in frame.phis().iter().zip(values) {
|
||||||
phi.add_incoming(&[(&value, ¤t_block)]);
|
phi.add_incoming(&[(&value, current_block)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.build_unconditional_branch(frame.br_dest());
|
builder.build_unconditional_branch(*frame.br_dest());
|
||||||
|
|
||||||
state.popn(value_len)?;
|
state.popn(value_len)?;
|
||||||
state.reachable = false;
|
state.reachable = false;
|
||||||
@ -1327,7 +1327,7 @@ impl<'ctx> FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator<'ct
|
|||||||
});
|
});
|
||||||
|
|
||||||
for (phi, value) in frame.phis().iter().zip(param_stack) {
|
for (phi, value) in frame.phis().iter().zip(param_stack) {
|
||||||
phi.add_incoming(&[(&value, ¤t_block)]);
|
phi.add_incoming(&[(&value, current_block)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let else_block = context.append_basic_block(function, "else");
|
let else_block = context.append_basic_block(function, "else");
|
||||||
@ -1338,8 +1338,8 @@ impl<'ctx> FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator<'ct
|
|||||||
intrinsics.i32_zero,
|
intrinsics.i32_zero,
|
||||||
&state.var_name(),
|
&state.var_name(),
|
||||||
);
|
);
|
||||||
builder.build_conditional_branch(cond_value, frame.br_dest(), &else_block);
|
builder.build_conditional_branch(cond_value, *frame.br_dest(), else_block);
|
||||||
builder.position_at_end(&else_block);
|
builder.position_at_end(else_block);
|
||||||
}
|
}
|
||||||
Operator::BrTable { ref table } => {
|
Operator::BrTable { ref table } => {
|
||||||
let current_block = builder.get_insert_block().ok_or(CodegenError {
|
let current_block = builder.get_insert_block().ok_or(CodegenError {
|
||||||
@ -1360,7 +1360,7 @@ impl<'ctx> FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator<'ct
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (phi, value) in default_frame.phis().iter().zip(args.iter()) {
|
for (phi, value) in default_frame.phis().iter().zip(args.iter()) {
|
||||||
phi.add_incoming(&[(value, ¤t_block)]);
|
phi.add_incoming(&[(value, current_block)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let cases: Vec<_> = label_depths
|
let cases: Vec<_> = label_depths
|
||||||
@ -1377,14 +1377,14 @@ impl<'ctx> FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator<'ct
|
|||||||
context.i32_type().const_int(case_index as u64, false);
|
context.i32_type().const_int(case_index as u64, false);
|
||||||
|
|
||||||
for (phi, value) in frame.phis().iter().zip(args.iter()) {
|
for (phi, value) in frame.phis().iter().zip(args.iter()) {
|
||||||
phi.add_incoming(&[(value, ¤t_block)]);
|
phi.add_incoming(&[(value, current_block)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((case_index_literal, frame.br_dest()))
|
Ok((case_index_literal, *frame.br_dest()))
|
||||||
})
|
})
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
builder.build_switch(index.into_int_value(), default_frame.br_dest(), &cases[..]);
|
builder.build_switch(index.into_int_value(), *default_frame.br_dest(), &cases[..]);
|
||||||
|
|
||||||
let args_len = args.len();
|
let args_len = args.len();
|
||||||
state.popn(args_len)?;
|
state.popn(args_len)?;
|
||||||
@ -1399,7 +1399,7 @@ impl<'ctx> FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator<'ct
|
|||||||
let end_block = context.append_basic_block(function, "if_end");
|
let end_block = context.append_basic_block(function, "if_end");
|
||||||
|
|
||||||
let end_phis = {
|
let end_phis = {
|
||||||
builder.position_at_end(&end_block);
|
builder.position_at_end(end_block);
|
||||||
|
|
||||||
let phis = if let Ok(wasmer_ty) = blocktype_to_type(ty) {
|
let phis = if let Ok(wasmer_ty) = blocktype_to_type(ty) {
|
||||||
let llvm_ty = type_to_llvm(intrinsics, wasmer_ty);
|
let llvm_ty = type_to_llvm(intrinsics, wasmer_ty);
|
||||||
@ -1411,7 +1411,7 @@ impl<'ctx> FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator<'ct
|
|||||||
SmallVec::new()
|
SmallVec::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
builder.position_at_end(¤t_block);
|
builder.position_at_end(current_block);
|
||||||
phis
|
phis
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1424,8 +1424,8 @@ impl<'ctx> FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator<'ct
|
|||||||
&state.var_name(),
|
&state.var_name(),
|
||||||
);
|
);
|
||||||
|
|
||||||
builder.build_conditional_branch(cond_value, &if_then_block, &if_else_block);
|
builder.build_conditional_branch(cond_value, if_then_block, if_else_block);
|
||||||
builder.position_at_end(&if_then_block);
|
builder.position_at_end(if_then_block);
|
||||||
state.push_if(if_then_block, if_else_block, end_block, end_phis);
|
state.push_if(if_then_block, if_else_block, end_block, end_phis);
|
||||||
}
|
}
|
||||||
Operator::Else => {
|
Operator::Else => {
|
||||||
@ -1439,10 +1439,10 @@ impl<'ctx> FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator<'ct
|
|||||||
let (value, info) = state.pop1_extra()?;
|
let (value, info) = state.pop1_extra()?;
|
||||||
let value =
|
let value =
|
||||||
apply_pending_canonicalization(builder, intrinsics, value, info);
|
apply_pending_canonicalization(builder, intrinsics, value, info);
|
||||||
phi.add_incoming(&[(&value, ¤t_block)])
|
phi.add_incoming(&[(&value, current_block)])
|
||||||
}
|
}
|
||||||
let frame = state.frame_at_depth(0)?;
|
let frame = state.frame_at_depth(0)?;
|
||||||
builder.build_unconditional_branch(frame.code_after());
|
builder.build_unconditional_branch(*frame.code_after());
|
||||||
}
|
}
|
||||||
|
|
||||||
let (if_else_block, if_else_state) = if let ControlFrame::IfElse {
|
let (if_else_block, if_else_state) = if let ControlFrame::IfElse {
|
||||||
@ -1458,7 +1458,7 @@ impl<'ctx> FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator<'ct
|
|||||||
|
|
||||||
*if_else_state = IfElseState::Else;
|
*if_else_state = IfElseState::Else;
|
||||||
|
|
||||||
builder.position_at_end(if_else_block);
|
builder.position_at_end(*if_else_block);
|
||||||
state.reachable = true;
|
state.reachable = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1473,10 +1473,10 @@ impl<'ctx> FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator<'ct
|
|||||||
let (value, info) = state.pop1_extra()?;
|
let (value, info) = state.pop1_extra()?;
|
||||||
let value =
|
let value =
|
||||||
apply_pending_canonicalization(builder, intrinsics, value, info);
|
apply_pending_canonicalization(builder, intrinsics, value, info);
|
||||||
phi.add_incoming(&[(&value, ¤t_block)]);
|
phi.add_incoming(&[(&value, current_block)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.build_unconditional_branch(frame.code_after());
|
builder.build_unconditional_branch(*frame.code_after());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let ControlFrame::IfElse {
|
if let ControlFrame::IfElse {
|
||||||
@ -1487,12 +1487,12 @@ impl<'ctx> FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator<'ct
|
|||||||
} = &frame
|
} = &frame
|
||||||
{
|
{
|
||||||
if let IfElseState::If = if_else_state {
|
if let IfElseState::If = if_else_state {
|
||||||
builder.position_at_end(if_else);
|
builder.position_at_end(*if_else);
|
||||||
builder.build_unconditional_branch(next);
|
builder.build_unconditional_branch(*next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.position_at_end(frame.code_after());
|
builder.position_at_end(*frame.code_after());
|
||||||
state.reset_stack(&frame);
|
state.reset_stack(&frame);
|
||||||
|
|
||||||
state.reachable = true;
|
state.reachable = true;
|
||||||
@ -1530,11 +1530,11 @@ impl<'ctx> FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator<'ct
|
|||||||
for phi in frame.phis().to_vec().iter() {
|
for phi in frame.phis().to_vec().iter() {
|
||||||
let (arg, info) = state.pop1_extra()?;
|
let (arg, info) = state.pop1_extra()?;
|
||||||
let arg = apply_pending_canonicalization(builder, intrinsics, arg, info);
|
let arg = apply_pending_canonicalization(builder, intrinsics, arg, info);
|
||||||
phi.add_incoming(&[(&arg, ¤t_block)]);
|
phi.add_incoming(&[(&arg, current_block)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let frame = state.outermost_frame()?;
|
let frame = state.outermost_frame()?;
|
||||||
builder.build_unconditional_branch(frame.br_dest());
|
builder.build_unconditional_branch(*frame.br_dest());
|
||||||
|
|
||||||
state.reachable = false;
|
state.reachable = false;
|
||||||
}
|
}
|
||||||
@ -2073,17 +2073,17 @@ impl<'ctx> FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator<'ct
|
|||||||
context.append_basic_block(function, "not_in_bounds_block");
|
context.append_basic_block(function, "not_in_bounds_block");
|
||||||
builder.build_conditional_branch(
|
builder.build_conditional_branch(
|
||||||
index_in_bounds,
|
index_in_bounds,
|
||||||
&in_bounds_continue_block,
|
in_bounds_continue_block,
|
||||||
¬_in_bounds_block,
|
not_in_bounds_block,
|
||||||
);
|
);
|
||||||
builder.position_at_end(¬_in_bounds_block);
|
builder.position_at_end(not_in_bounds_block);
|
||||||
builder.build_call(
|
builder.build_call(
|
||||||
intrinsics.throw_trap,
|
intrinsics.throw_trap,
|
||||||
&[intrinsics.trap_call_indirect_oob],
|
&[intrinsics.trap_call_indirect_oob],
|
||||||
"throw",
|
"throw",
|
||||||
);
|
);
|
||||||
builder.build_unreachable();
|
builder.build_unreachable();
|
||||||
builder.position_at_end(&in_bounds_continue_block);
|
builder.position_at_end(in_bounds_continue_block);
|
||||||
|
|
||||||
// Next, check if the signature id is correct.
|
// Next, check if the signature id is correct.
|
||||||
|
|
||||||
@ -2114,18 +2114,18 @@ impl<'ctx> FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator<'ct
|
|||||||
context.append_basic_block(function, "sigindices_notequal_block");
|
context.append_basic_block(function, "sigindices_notequal_block");
|
||||||
builder.build_conditional_branch(
|
builder.build_conditional_branch(
|
||||||
sigindices_equal,
|
sigindices_equal,
|
||||||
&continue_block,
|
continue_block,
|
||||||
&sigindices_notequal_block,
|
sigindices_notequal_block,
|
||||||
);
|
);
|
||||||
|
|
||||||
builder.position_at_end(&sigindices_notequal_block);
|
builder.position_at_end(sigindices_notequal_block);
|
||||||
builder.build_call(
|
builder.build_call(
|
||||||
intrinsics.throw_trap,
|
intrinsics.throw_trap,
|
||||||
&[intrinsics.trap_call_indirect_sig],
|
&[intrinsics.trap_call_indirect_sig],
|
||||||
"throw",
|
"throw",
|
||||||
);
|
);
|
||||||
builder.build_unreachable();
|
builder.build_unreachable();
|
||||||
builder.position_at_end(&continue_block);
|
builder.position_at_end(continue_block);
|
||||||
|
|
||||||
let wasmer_fn_sig = &info.signatures[sig_index];
|
let wasmer_fn_sig = &info.signatures[sig_index];
|
||||||
let fn_ty = signatures[sig_index];
|
let fn_ty = signatures[sig_index];
|
||||||
@ -8763,10 +8763,10 @@ impl<'ctx> ModuleCodeGenerator<LLVMFunctionCodeGenerator<'ctx>, LLVMBackend, Cod
|
|||||||
let mut state: State<'ctx> = State::new();
|
let mut state: State<'ctx> = State::new();
|
||||||
let entry_block = context.append_basic_block(*function, "entry");
|
let entry_block = context.append_basic_block(*function, "entry");
|
||||||
let alloca_builder = context.create_builder();
|
let alloca_builder = context.create_builder();
|
||||||
alloca_builder.position_at_end(&entry_block);
|
alloca_builder.position_at_end(entry_block);
|
||||||
|
|
||||||
let return_block = context.append_basic_block(*function, "return");
|
let return_block = context.append_basic_block(*function, "return");
|
||||||
builder.position_at_end(&return_block);
|
builder.position_at_end(return_block);
|
||||||
|
|
||||||
let phis: SmallVec<[PhiValue; 1]> = func_sig
|
let phis: SmallVec<[PhiValue; 1]> = func_sig
|
||||||
.returns()
|
.returns()
|
||||||
@ -8776,7 +8776,7 @@ impl<'ctx> ModuleCodeGenerator<LLVMFunctionCodeGenerator<'ctx>, LLVMBackend, Cod
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
state.push_block(return_block, phis);
|
state.push_block(return_block, phis);
|
||||||
builder.position_at_end(&entry_block);
|
builder.position_at_end(entry_block);
|
||||||
|
|
||||||
let mut locals = Vec::new();
|
let mut locals = Vec::new();
|
||||||
locals.extend(
|
locals.extend(
|
||||||
@ -8984,7 +8984,7 @@ impl<'ctx> ModuleCodeGenerator<LLVMFunctionCodeGenerator<'ctx>, LLVMBackend, Cod
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn feed_import_function(&mut self) -> Result<(), CodegenError> {
|
fn feed_import_function(&mut self, _sigindex: SigIndex) -> Result<(), CodegenError> {
|
||||||
self.func_import_count += 1;
|
self.func_import_count += 1;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -10,20 +10,20 @@ use std::ops::{BitAnd, BitOr, BitOrAssign};
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ControlFrame<'ctx> {
|
pub enum ControlFrame<'ctx> {
|
||||||
Block {
|
Block {
|
||||||
next: BasicBlock,
|
next: BasicBlock<'ctx>,
|
||||||
phis: SmallVec<[PhiValue<'ctx>; 1]>,
|
phis: SmallVec<[PhiValue<'ctx>; 1]>,
|
||||||
stack_size_snapshot: usize,
|
stack_size_snapshot: usize,
|
||||||
},
|
},
|
||||||
Loop {
|
Loop {
|
||||||
body: BasicBlock,
|
body: BasicBlock<'ctx>,
|
||||||
next: BasicBlock,
|
next: BasicBlock<'ctx>,
|
||||||
phis: SmallVec<[PhiValue<'ctx>; 1]>,
|
phis: SmallVec<[PhiValue<'ctx>; 1]>,
|
||||||
stack_size_snapshot: usize,
|
stack_size_snapshot: usize,
|
||||||
},
|
},
|
||||||
IfElse {
|
IfElse {
|
||||||
if_then: BasicBlock,
|
if_then: BasicBlock<'ctx>,
|
||||||
if_else: BasicBlock,
|
if_else: BasicBlock<'ctx>,
|
||||||
next: BasicBlock,
|
next: BasicBlock<'ctx>,
|
||||||
phis: SmallVec<[PhiValue<'ctx>; 1]>,
|
phis: SmallVec<[PhiValue<'ctx>; 1]>,
|
||||||
stack_size_snapshot: usize,
|
stack_size_snapshot: usize,
|
||||||
if_else_state: IfElseState,
|
if_else_state: IfElseState,
|
||||||
@ -37,7 +37,7 @@ pub enum IfElseState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx> ControlFrame<'ctx> {
|
impl<'ctx> ControlFrame<'ctx> {
|
||||||
pub fn code_after(&self) -> &BasicBlock {
|
pub fn code_after(&self) -> &BasicBlock<'ctx> {
|
||||||
match self {
|
match self {
|
||||||
ControlFrame::Block { ref next, .. }
|
ControlFrame::Block { ref next, .. }
|
||||||
| ControlFrame::Loop { ref next, .. }
|
| ControlFrame::Loop { ref next, .. }
|
||||||
@ -45,7 +45,7 @@ impl<'ctx> ControlFrame<'ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn br_dest(&self) -> &BasicBlock {
|
pub fn br_dest(&self) -> &BasicBlock<'ctx> {
|
||||||
match self {
|
match self {
|
||||||
ControlFrame::Block { ref next, .. } | ControlFrame::IfElse { ref next, .. } => next,
|
ControlFrame::Block { ref next, .. } | ControlFrame::IfElse { ref next, .. } => next,
|
||||||
ControlFrame::Loop { ref body, .. } => body,
|
ControlFrame::Loop { ref body, .. } => body,
|
||||||
@ -367,7 +367,7 @@ impl<'ctx> State<'ctx> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_block(&mut self, next: BasicBlock, phis: SmallVec<[PhiValue<'ctx>; 1]>) {
|
pub fn push_block(&mut self, next: BasicBlock<'ctx>, phis: SmallVec<[PhiValue<'ctx>; 1]>) {
|
||||||
self.control_stack.push(ControlFrame::Block {
|
self.control_stack.push(ControlFrame::Block {
|
||||||
next,
|
next,
|
||||||
phis,
|
phis,
|
||||||
@ -377,8 +377,8 @@ impl<'ctx> State<'ctx> {
|
|||||||
|
|
||||||
pub fn push_loop(
|
pub fn push_loop(
|
||||||
&mut self,
|
&mut self,
|
||||||
body: BasicBlock,
|
body: BasicBlock<'ctx>,
|
||||||
next: BasicBlock,
|
next: BasicBlock<'ctx>,
|
||||||
phis: SmallVec<[PhiValue<'ctx>; 1]>,
|
phis: SmallVec<[PhiValue<'ctx>; 1]>,
|
||||||
) {
|
) {
|
||||||
self.control_stack.push(ControlFrame::Loop {
|
self.control_stack.push(ControlFrame::Loop {
|
||||||
@ -391,9 +391,9 @@ impl<'ctx> State<'ctx> {
|
|||||||
|
|
||||||
pub fn push_if(
|
pub fn push_if(
|
||||||
&mut self,
|
&mut self,
|
||||||
if_then: BasicBlock,
|
if_then: BasicBlock<'ctx>,
|
||||||
if_else: BasicBlock,
|
if_else: BasicBlock<'ctx>,
|
||||||
next: BasicBlock,
|
next: BasicBlock<'ctx>,
|
||||||
phis: SmallVec<[PhiValue<'ctx>; 1]>,
|
phis: SmallVec<[PhiValue<'ctx>; 1]>,
|
||||||
) {
|
) {
|
||||||
self.control_stack.push(ControlFrame::IfElse {
|
self.control_stack.push(ControlFrame::IfElse {
|
||||||
|
@ -55,7 +55,7 @@ fn generate_trampoline<'ctx>(
|
|||||||
intrinsics: &Intrinsics<'ctx>,
|
intrinsics: &Intrinsics<'ctx>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let entry_block = context.append_basic_block(trampoline_func, "entry");
|
let entry_block = context.append_basic_block(trampoline_func, "entry");
|
||||||
builder.position_at_end(&entry_block);
|
builder.position_at_end(entry_block);
|
||||||
|
|
||||||
let (vmctx_ptr, func_ptr, args_ptr, returns_ptr) = match trampoline_func.get_params().as_slice()
|
let (vmctx_ptr, func_ptr, args_ptr, returns_ptr) = match trampoline_func.get_params().as_slice()
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wasmer-middleware-common-tests"
|
name = "wasmer-middleware-common-tests"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
repository = "https://github.com/wasmerio/wasmer"
|
repository = "https://github.com/wasmerio/wasmer"
|
||||||
@ -8,11 +8,11 @@ license = "MIT"
|
|||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.14.1" }
|
wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" }
|
||||||
wasmer-middleware-common = { path = "../middleware-common", version = "0.14.1" }
|
wasmer-middleware-common = { path = "../middleware-common", version = "0.16.2" }
|
||||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.14.1", optional = true }
|
wasmer-clif-backend = { path = "../clif-backend", version = "0.16.2", optional = true }
|
||||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.14.1", features = ["test"], optional = true }
|
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.2", features = ["test"], optional = true }
|
||||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.14.1", optional = true }
|
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.2", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
clif = ["wasmer-clif-backend"]
|
clif = ["wasmer-clif-backend"]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wasmer-middleware-common"
|
name = "wasmer-middleware-common"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
repository = "https://github.com/wasmerio/wasmer"
|
repository = "https://github.com/wasmerio/wasmer"
|
||||||
description = "Wasmer runtime common middlewares"
|
description = "Wasmer runtime common middlewares"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
@ -10,4 +10,4 @@ categories = ["wasm"]
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.14.1" }
|
wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" }
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wasmer-runtime-c-api"
|
name = "wasmer-runtime-c-api"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
description = "Wasmer C API library"
|
description = "Wasmer C API library"
|
||||||
documentation = "https://wasmerio.github.io/wasmer/c/runtime-c-api/"
|
documentation = "https://wasmerio.github.io/wasmer/c/runtime-c-api/"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
@ -20,22 +20,22 @@ libc = "0.2.60"
|
|||||||
[dependencies.wasmer-runtime]
|
[dependencies.wasmer-runtime]
|
||||||
default-features = false
|
default-features = false
|
||||||
path = "../runtime"
|
path = "../runtime"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
|
|
||||||
[dependencies.wasmer-runtime-core]
|
[dependencies.wasmer-runtime-core]
|
||||||
default-features = false
|
default-features = false
|
||||||
path = "../runtime-core"
|
path = "../runtime-core"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
|
|
||||||
[dependencies.wasmer-wasi]
|
[dependencies.wasmer-wasi]
|
||||||
default-features = false
|
default-features = false
|
||||||
path = "../wasi"
|
path = "../wasi"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.wasmer-emscripten]
|
[dependencies.wasmer-emscripten]
|
||||||
path = "../emscripten"
|
path = "../emscripten"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
@ -34,6 +34,8 @@ pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_add_context_trampoline
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a callinfo trampoline to the builder.
|
/// Adds a callinfo trampoline to the builder.
|
||||||
|
///
|
||||||
|
/// Deprecated. In a future version `DynamicFunc::new` will be exposed to the C API and should be used instead of this function.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_add_callinfo_trampoline(
|
pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_add_callinfo_trampoline(
|
||||||
@ -42,8 +44,14 @@ pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_add_callinfo_trampolin
|
|||||||
ctx: *const c_void,
|
ctx: *const c_void,
|
||||||
num_params: u32,
|
num_params: u32,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
|
use wasmer_runtime_core::types::Type;
|
||||||
let builder = &mut *(builder as *mut TrampolineBufferBuilder);
|
let builder = &mut *(builder as *mut TrampolineBufferBuilder);
|
||||||
builder.add_callinfo_trampoline(mem::transmute(func), ctx as *const CallContext, num_params)
|
builder.add_callinfo_trampoline(
|
||||||
|
mem::transmute(func),
|
||||||
|
ctx as *const CallContext,
|
||||||
|
&vec![Type::I64; num_params as usize],
|
||||||
|
&[Type::I64],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finalizes the trampoline builder into an executable buffer.
|
/// Finalizes the trampoline builder into an executable buffer.
|
||||||
|
@ -1386,6 +1386,8 @@ wasmer_result_t wasmer_table_new(wasmer_table_t **table, wasmer_limits_t limits)
|
|||||||
#if (!defined(_WIN32) && defined(ARCH_X86_64))
|
#if (!defined(_WIN32) && defined(ARCH_X86_64))
|
||||||
/**
|
/**
|
||||||
* Adds a callinfo trampoline to the builder.
|
* Adds a callinfo trampoline to the builder.
|
||||||
|
*
|
||||||
|
* Deprecated. In a future version `DynamicFunc::new` will be exposed to the C API and should be used instead of this function.
|
||||||
*/
|
*/
|
||||||
uintptr_t wasmer_trampoline_buffer_builder_add_callinfo_trampoline(wasmer_trampoline_buffer_builder_t *builder,
|
uintptr_t wasmer_trampoline_buffer_builder_add_callinfo_trampoline(wasmer_trampoline_buffer_builder_t *builder,
|
||||||
const wasmer_trampoline_callable_t *func,
|
const wasmer_trampoline_callable_t *func,
|
||||||
|
@ -1146,6 +1146,8 @@ wasmer_result_t wasmer_table_new(wasmer_table_t **table, wasmer_limits_t limits)
|
|||||||
|
|
||||||
#if (!defined(_WIN32) && defined(ARCH_X86_64))
|
#if (!defined(_WIN32) && defined(ARCH_X86_64))
|
||||||
/// Adds a callinfo trampoline to the builder.
|
/// Adds a callinfo trampoline to the builder.
|
||||||
|
///
|
||||||
|
/// Deprecated. In a future version `DynamicFunc::new` will be exposed to the C API and should be used instead of this function.
|
||||||
uintptr_t wasmer_trampoline_buffer_builder_add_callinfo_trampoline(wasmer_trampoline_buffer_builder_t *builder,
|
uintptr_t wasmer_trampoline_buffer_builder_add_callinfo_trampoline(wasmer_trampoline_buffer_builder_t *builder,
|
||||||
const wasmer_trampoline_callable_t *func,
|
const wasmer_trampoline_callable_t *func,
|
||||||
const void *ctx,
|
const void *ctx,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wasmer-runtime-core-tests"
|
name = "wasmer-runtime-core-tests"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
description = "Tests for the Wasmer runtime core crate"
|
description = "Tests for the Wasmer runtime core crate"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||||
@ -9,10 +9,10 @@ publish = false
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wabt = "0.9.1"
|
wabt = "0.9.1"
|
||||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.14.1" }
|
wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" }
|
||||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.14.1", optional = true }
|
wasmer-clif-backend = { path = "../clif-backend", version = "0.16.2", optional = true }
|
||||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.14.1", optional = true }
|
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.2", optional = true }
|
||||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.14.1", features = ["test"], optional = true }
|
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.2", features = ["test"], optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["backend-cranelift"]
|
default = ["backend-cranelift"]
|
||||||
|
@ -1,14 +1,22 @@
|
|||||||
|
use std::{convert::TryInto, sync::Arc};
|
||||||
use wasmer_runtime_core::{
|
use wasmer_runtime_core::{
|
||||||
compile_with, error::RuntimeError, imports, memory::Memory, typed_func::Func,
|
compile_with,
|
||||||
types::MemoryDescriptor, units::Pages, vm, Instance,
|
error::RuntimeError,
|
||||||
|
imports,
|
||||||
|
memory::Memory,
|
||||||
|
typed_func::{DynamicFunc, Func},
|
||||||
|
types::{FuncSig, MemoryDescriptor, Type, Value},
|
||||||
|
units::Pages,
|
||||||
|
vm, Instance,
|
||||||
};
|
};
|
||||||
use wasmer_runtime_core_tests::{get_compiler, wat2wasm};
|
use wasmer_runtime_core_tests::{get_compiler, wat2wasm};
|
||||||
|
|
||||||
macro_rules! call_and_assert {
|
macro_rules! call_and_assert {
|
||||||
($instance:ident, $function:ident, $expected_value:expr) => {
|
($instance:ident, $function:ident( $( $inputs:ty ),* ) -> $output:ty, ( $( $arguments:expr ),* ) == $expected_value:expr) => {
|
||||||
let $function: Func<i32, i32> = $instance.func(stringify!($function)).unwrap();
|
#[allow(unused_parens)]
|
||||||
|
let $function: Func<( $( $inputs ),* ), $output> = $instance.func(stringify!($function)).expect(concat!("Failed to get the `", stringify!($function), "` export function."));
|
||||||
|
|
||||||
let result = $function.call(1);
|
let result = $function.call( $( $arguments ),* );
|
||||||
|
|
||||||
match (result, $expected_value) {
|
match (result, $expected_value) {
|
||||||
(Ok(value), expected_value) => assert_eq!(
|
(Ok(value), expected_value) => assert_eq!(
|
||||||
@ -68,6 +76,12 @@ fn imported_functions_forms(test: &dyn Fn(&Instance)) {
|
|||||||
(import "env" "memory" (memory 1 1))
|
(import "env" "memory" (memory 1 1))
|
||||||
(import "env" "callback_fn" (func $callback_fn (type $type)))
|
(import "env" "callback_fn" (func $callback_fn (type $type)))
|
||||||
(import "env" "callback_closure" (func $callback_closure (type $type)))
|
(import "env" "callback_closure" (func $callback_closure (type $type)))
|
||||||
|
(import "env" "callback_fn_dynamic" (func $callback_fn_dynamic (type $type)))
|
||||||
|
(import "env" "callback_closure_dynamic_0" (func $callback_closure_dynamic_0))
|
||||||
|
(import "env" "callback_closure_dynamic_1" (func $callback_closure_dynamic_1 (param i32) (result i32)))
|
||||||
|
(import "env" "callback_closure_dynamic_2" (func $callback_closure_dynamic_2 (param i32 i64) (result i64)))
|
||||||
|
(import "env" "callback_closure_dynamic_3" (func $callback_closure_dynamic_3 (param i32 i64 f32) (result f32)))
|
||||||
|
(import "env" "callback_closure_dynamic_4" (func $callback_closure_dynamic_4 (param i32 i64 f32 f64) (result f64)))
|
||||||
(import "env" "callback_closure_with_env" (func $callback_closure_with_env (type $type)))
|
(import "env" "callback_closure_with_env" (func $callback_closure_with_env (type $type)))
|
||||||
(import "env" "callback_fn_with_vmctx" (func $callback_fn_with_vmctx (type $type)))
|
(import "env" "callback_fn_with_vmctx" (func $callback_fn_with_vmctx (type $type)))
|
||||||
(import "env" "callback_closure_with_vmctx" (func $callback_closure_with_vmctx (type $type)))
|
(import "env" "callback_closure_with_vmctx" (func $callback_closure_with_vmctx (type $type)))
|
||||||
@ -86,6 +100,35 @@ fn imported_functions_forms(test: &dyn Fn(&Instance)) {
|
|||||||
get_local 0
|
get_local 0
|
||||||
call $callback_closure)
|
call $callback_closure)
|
||||||
|
|
||||||
|
(func (export "function_fn_dynamic") (type $type)
|
||||||
|
get_local 0
|
||||||
|
call $callback_fn_dynamic)
|
||||||
|
|
||||||
|
(func (export "function_closure_dynamic_0")
|
||||||
|
call $callback_closure_dynamic_0)
|
||||||
|
|
||||||
|
(func (export "function_closure_dynamic_1") (param i32) (result i32)
|
||||||
|
get_local 0
|
||||||
|
call $callback_closure_dynamic_1)
|
||||||
|
|
||||||
|
(func (export "function_closure_dynamic_2") (param i32 i64) (result i64)
|
||||||
|
get_local 0
|
||||||
|
get_local 1
|
||||||
|
call $callback_closure_dynamic_2)
|
||||||
|
|
||||||
|
(func (export "function_closure_dynamic_3") (param i32 i64 f32) (result f32)
|
||||||
|
get_local 0
|
||||||
|
get_local 1
|
||||||
|
get_local 2
|
||||||
|
call $callback_closure_dynamic_3)
|
||||||
|
|
||||||
|
(func (export "function_closure_dynamic_4") (param i32 i64 f32 f64) (result f64)
|
||||||
|
get_local 0
|
||||||
|
get_local 1
|
||||||
|
get_local 2
|
||||||
|
get_local 3
|
||||||
|
call $callback_closure_dynamic_4)
|
||||||
|
|
||||||
(func (export "function_closure_with_env") (type $type)
|
(func (export "function_closure_with_env") (type $type)
|
||||||
get_local 0
|
get_local 0
|
||||||
call $callback_closure_with_env)
|
call $callback_closure_with_env)
|
||||||
@ -142,6 +185,76 @@ fn imported_functions_forms(test: &dyn Fn(&Instance)) {
|
|||||||
Ok(n + 1)
|
Ok(n + 1)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
// Regular polymorphic function.
|
||||||
|
"callback_fn_dynamic" => DynamicFunc::new(
|
||||||
|
Arc::new(FuncSig::new(vec![Type::I32], vec![Type::I32])),
|
||||||
|
callback_fn_dynamic,
|
||||||
|
),
|
||||||
|
|
||||||
|
// Polymorphic closure “closures”.
|
||||||
|
"callback_closure_dynamic_0" => DynamicFunc::new(
|
||||||
|
Arc::new(FuncSig::new(vec![], vec![])),
|
||||||
|
|_, inputs: &[Value]| -> Vec<Value> {
|
||||||
|
assert!(inputs.is_empty());
|
||||||
|
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
),
|
||||||
|
"callback_closure_dynamic_1" => DynamicFunc::new(
|
||||||
|
Arc::new(FuncSig::new(vec![Type::I32], vec![Type::I32])),
|
||||||
|
move |vmctx: &mut vm::Ctx, inputs: &[Value]| -> Vec<Value> {
|
||||||
|
assert_eq!(inputs.len(), 1);
|
||||||
|
|
||||||
|
let memory = vmctx.memory(0);
|
||||||
|
let shift_ = shift + memory.view::<i32>()[0].get();
|
||||||
|
let n: i32 = (&inputs[0]).try_into().unwrap();
|
||||||
|
|
||||||
|
vec![Value::I32(shift_ + n)]
|
||||||
|
}
|
||||||
|
),
|
||||||
|
"callback_closure_dynamic_2" => DynamicFunc::new(
|
||||||
|
Arc::new(FuncSig::new(vec![Type::I32, Type::I64], vec![Type::I64])),
|
||||||
|
move |vmctx: &mut vm::Ctx, inputs: &[Value]| -> Vec<Value> {
|
||||||
|
assert_eq!(inputs.len(), 2);
|
||||||
|
|
||||||
|
let memory = vmctx.memory(0);
|
||||||
|
let shift_ = shift + memory.view::<i32>()[0].get();
|
||||||
|
let i: i32 = (&inputs[0]).try_into().unwrap();
|
||||||
|
let j: i64 = (&inputs[1]).try_into().unwrap();
|
||||||
|
|
||||||
|
vec![Value::I64(shift_ as i64 + i as i64 + j)]
|
||||||
|
}
|
||||||
|
),
|
||||||
|
"callback_closure_dynamic_3" => DynamicFunc::new(
|
||||||
|
Arc::new(FuncSig::new(vec![Type::I32, Type::I64, Type::F32], vec![Type::F32])),
|
||||||
|
move |vmctx: &mut vm::Ctx, inputs: &[Value]| -> Vec<Value> {
|
||||||
|
assert_eq!(inputs.len(), 3);
|
||||||
|
|
||||||
|
let memory = vmctx.memory(0);
|
||||||
|
let shift_ = shift + memory.view::<i32>()[0].get();
|
||||||
|
let i: i32 = (&inputs[0]).try_into().unwrap();
|
||||||
|
let j: i64 = (&inputs[1]).try_into().unwrap();
|
||||||
|
let k: f32 = (&inputs[2]).try_into().unwrap();
|
||||||
|
|
||||||
|
vec![Value::F32(shift_ as f32 + i as f32 + j as f32 + k)]
|
||||||
|
}
|
||||||
|
),
|
||||||
|
"callback_closure_dynamic_4" => DynamicFunc::new(
|
||||||
|
Arc::new(FuncSig::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![Type::F64])),
|
||||||
|
move |vmctx: &mut vm::Ctx, inputs: &[Value]| -> Vec<Value> {
|
||||||
|
assert_eq!(inputs.len(), 4);
|
||||||
|
|
||||||
|
let memory = vmctx.memory(0);
|
||||||
|
let shift_ = shift + memory.view::<i32>()[0].get();
|
||||||
|
let i: i32 = (&inputs[0]).try_into().unwrap();
|
||||||
|
let j: i64 = (&inputs[1]).try_into().unwrap();
|
||||||
|
let k: f32 = (&inputs[2]).try_into().unwrap();
|
||||||
|
let l: f64 = (&inputs[3]).try_into().unwrap();
|
||||||
|
|
||||||
|
vec![Value::F64(shift_ as f64 + i as f64 + j as f64 + k as f64 + l)]
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|
||||||
// Closure with a captured environment (a single variable + an instance of `Memory`).
|
// Closure with a captured environment (a single variable + an instance of `Memory`).
|
||||||
"callback_closure_with_env" => Func::new(move |n: i32| -> Result<i32, ()> {
|
"callback_closure_with_env" => Func::new(move |n: i32| -> Result<i32, ()> {
|
||||||
let shift_ = shift + memory.view::<i32>()[0].get();
|
let shift_ = shift + memory.view::<i32>()[0].get();
|
||||||
@ -205,6 +318,13 @@ fn callback_fn(n: i32) -> Result<i32, ()> {
|
|||||||
Ok(n + 1)
|
Ok(n + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn callback_fn_dynamic(_: &mut vm::Ctx, inputs: &[Value]) -> Vec<Value> {
|
||||||
|
match inputs[0] {
|
||||||
|
Value::I32(x) => vec![Value::I32(x + 1)],
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn callback_fn_with_vmctx(vmctx: &mut vm::Ctx, n: i32) -> Result<i32, ()> {
|
fn callback_fn_with_vmctx(vmctx: &mut vm::Ctx, n: i32) -> Result<i32, ()> {
|
||||||
let memory = vmctx.memory(0);
|
let memory = vmctx.memory(0);
|
||||||
let shift_: i32 = memory.view()[0].get();
|
let shift_: i32 = memory.view()[0].get();
|
||||||
@ -224,56 +344,82 @@ fn callback_fn_trap_with_vmctx(vmctx: &mut vm::Ctx, n: i32) -> Result<i32, Strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! test {
|
macro_rules! test {
|
||||||
($test_name:ident, $function:ident, $expected_value:expr) => {
|
($test_name:ident, $function:ident( $( $inputs:ty ),* ) -> $output:ty, ( $( $arguments:expr ),* ) == $expected_value:expr) => {
|
||||||
#[test]
|
#[test]
|
||||||
fn $test_name() {
|
fn $test_name() {
|
||||||
imported_functions_forms(&|instance| {
|
imported_functions_forms(&|instance| {
|
||||||
call_and_assert!(instance, $function, $expected_value);
|
call_and_assert!(instance, $function( $( $inputs ),* ) -> $output, ( $( $arguments ),* ) == $expected_value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
test!(test_fn, function_fn, Ok(2));
|
test!(test_fn, function_fn(i32) -> i32, (1) == Ok(2));
|
||||||
test!(test_closure, function_closure, Ok(2));
|
test!(test_closure, function_closure(i32) -> i32, (1) == Ok(2));
|
||||||
|
test!(test_fn_dynamic, function_fn_dynamic(i32) -> i32, (1) == Ok(2));
|
||||||
|
test!(
|
||||||
|
test_closure_dynamic_0,
|
||||||
|
function_closure_dynamic_0(()) -> (),
|
||||||
|
() == Ok(())
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
test_closure_dynamic_1,
|
||||||
|
function_closure_dynamic_1(i32) -> i32,
|
||||||
|
(1) == Ok(1 + shift + SHIFT)
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
test_closure_dynamic_2,
|
||||||
|
function_closure_dynamic_2(i32, i64) -> i64,
|
||||||
|
(1, 2) == Ok(1 + 2 + shift as i64 + SHIFT as i64)
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
test_closure_dynamic_3,
|
||||||
|
function_closure_dynamic_3(i32, i64, f32) -> f32,
|
||||||
|
(1, 2, 3.) == Ok(1. + 2. + 3. + shift as f32 + SHIFT as f32)
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
test_closure_dynamic_4,
|
||||||
|
function_closure_dynamic_4(i32, i64, f32, f64) -> f64,
|
||||||
|
(1, 2, 3., 4.) == Ok(1. + 2. + 3. + 4. + shift as f64 + SHIFT as f64)
|
||||||
|
);
|
||||||
test!(
|
test!(
|
||||||
test_closure_with_env,
|
test_closure_with_env,
|
||||||
function_closure_with_env,
|
function_closure_with_env(i32) -> i32,
|
||||||
Ok(2 + shift + SHIFT)
|
(1) == Ok(2 + shift + SHIFT)
|
||||||
);
|
);
|
||||||
test!(test_fn_with_vmctx, function_fn_with_vmctx, Ok(2 + SHIFT));
|
test!(test_fn_with_vmctx, function_fn_with_vmctx(i32) -> i32, (1) == Ok(2 + SHIFT));
|
||||||
test!(
|
test!(
|
||||||
test_closure_with_vmctx,
|
test_closure_with_vmctx,
|
||||||
function_closure_with_vmctx,
|
function_closure_with_vmctx(i32) -> i32,
|
||||||
Ok(2 + SHIFT)
|
(1) == Ok(2 + SHIFT)
|
||||||
);
|
);
|
||||||
test!(
|
test!(
|
||||||
test_closure_with_vmctx_and_env,
|
test_closure_with_vmctx_and_env,
|
||||||
function_closure_with_vmctx_and_env,
|
function_closure_with_vmctx_and_env(i32) -> i32,
|
||||||
Ok(2 + shift + SHIFT)
|
(1) == Ok(2 + shift + SHIFT)
|
||||||
);
|
);
|
||||||
test!(
|
test!(
|
||||||
test_fn_trap,
|
test_fn_trap,
|
||||||
function_fn_trap,
|
function_fn_trap(i32) -> i32,
|
||||||
Err(RuntimeError(Box::new(format!("foo {}", 2))))
|
(1) == Err(RuntimeError(Box::new(format!("foo {}", 2))))
|
||||||
);
|
);
|
||||||
test!(
|
test!(
|
||||||
test_closure_trap,
|
test_closure_trap,
|
||||||
function_closure_trap,
|
function_closure_trap(i32) -> i32,
|
||||||
Err(RuntimeError(Box::new(format!("bar {}", 2))))
|
(1) == Err(RuntimeError(Box::new(format!("bar {}", 2))))
|
||||||
);
|
);
|
||||||
test!(
|
test!(
|
||||||
test_fn_trap_with_vmctx,
|
test_fn_trap_with_vmctx,
|
||||||
function_fn_trap_with_vmctx,
|
function_fn_trap_with_vmctx(i32) -> i32,
|
||||||
Err(RuntimeError(Box::new(format!("baz {}", 2 + SHIFT))))
|
(1) == Err(RuntimeError(Box::new(format!("baz {}", 2 + SHIFT))))
|
||||||
);
|
);
|
||||||
test!(
|
test!(
|
||||||
test_closure_trap_with_vmctx,
|
test_closure_trap_with_vmctx,
|
||||||
function_closure_trap_with_vmctx,
|
function_closure_trap_with_vmctx(i32) -> i32,
|
||||||
Err(RuntimeError(Box::new(format!("qux {}", 2 + SHIFT))))
|
(1) == Err(RuntimeError(Box::new(format!("qux {}", 2 + SHIFT))))
|
||||||
);
|
);
|
||||||
test!(
|
test!(
|
||||||
test_closure_trap_with_vmctx_and_env,
|
test_closure_trap_with_vmctx_and_env,
|
||||||
function_closure_trap_with_vmctx_and_env,
|
function_closure_trap_with_vmctx_and_env(i32) -> i32,
|
||||||
Err(RuntimeError(Box::new(format!("! {}", 2 + shift + SHIFT))))
|
(1) == Err(RuntimeError(Box::new(format!("! {}", 2 + shift + SHIFT))))
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wasmer-runtime-core"
|
name = "wasmer-runtime-core"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
description = "Wasmer runtime core library"
|
description = "Wasmer runtime core library"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||||
@ -59,3 +59,5 @@ generate-debug-information = ["wasm-debug"]
|
|||||||
# don't export symbols related to the GDB JIT interafce, LLVM or some other native
|
# don't export symbols related to the GDB JIT interafce, LLVM or some other native
|
||||||
# code will be providing them
|
# code will be providing them
|
||||||
generate-debug-information-no-export-symbols = []
|
generate-debug-information-no-export-symbols = []
|
||||||
|
# enable DynamicFunc's for closures with captured environment.
|
||||||
|
dynamicfunc-fat-closures = []
|
||||||
|
@ -143,7 +143,7 @@ pub trait ModuleCodeGenerator<FCG: FunctionCodeGenerator<E>, RM: RunnableModule,
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
/// Adds an import function.
|
/// Adds an import function.
|
||||||
fn feed_import_function(&mut self) -> Result<(), E>;
|
fn feed_import_function(&mut self, _sigindex: SigIndex) -> Result<(), E>;
|
||||||
/// Sets the signatures.
|
/// Sets the signatures.
|
||||||
fn feed_signatures(&mut self, signatures: Map<SigIndex, FuncSig>) -> Result<(), E>;
|
fn feed_signatures(&mut self, signatures: Map<SigIndex, FuncSig>) -> Result<(), E>;
|
||||||
/// Sets function signatures.
|
/// Sets function signatures.
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
//! The loader module functions are used to load an instance.
|
//! The loader module functions are used to load an instance.
|
||||||
use crate::{backend::RunnableModule, module::ModuleInfo, types::Type, types::Value, vm::Ctx};
|
use crate::{backend::RunnableModule, module::ModuleInfo, types::Type, types::Value, vm::Ctx};
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use libc::{mmap, mprotect, munmap, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_READ, PROT_WRITE};
|
use libc::{
|
||||||
|
mmap, mprotect, munmap, MAP_ANON, MAP_NORESERVE, MAP_PRIVATE, PROT_EXEC, PROT_READ, PROT_WRITE,
|
||||||
|
};
|
||||||
use std::{
|
use std::{
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
@ -138,12 +140,12 @@ impl CodeMemory {
|
|||||||
unimplemented!("CodeMemory::new");
|
unimplemented!("CodeMemory::new");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Makes this code memory executable.
|
/// Makes this code memory executable and not writable.
|
||||||
pub fn make_executable(&self) {
|
pub fn make_executable(&self) {
|
||||||
unimplemented!("CodeMemory::make_executable");
|
unimplemented!("CodeMemory::make_executable");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Makes this code memory writable.
|
/// Makes this code memory writable and not executable.
|
||||||
pub fn make_writable(&self) {
|
pub fn make_writable(&self) {
|
||||||
unimplemented!("CodeMemory::make_writable");
|
unimplemented!("CodeMemory::make_writable");
|
||||||
}
|
}
|
||||||
@ -169,7 +171,7 @@ impl CodeMemory {
|
|||||||
std::ptr::null_mut(),
|
std::ptr::null_mut(),
|
||||||
size,
|
size,
|
||||||
PROT_READ | PROT_WRITE,
|
PROT_READ | PROT_WRITE,
|
||||||
MAP_PRIVATE | MAP_ANON,
|
MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
|
||||||
-1,
|
-1,
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
@ -183,19 +185,33 @@ impl CodeMemory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Makes this code memory executable.
|
/// Makes this code memory executable and not writable.
|
||||||
pub fn make_executable(&self) {
|
pub fn make_executable(&self) {
|
||||||
if unsafe { mprotect(self.ptr as _, self.size, PROT_READ | PROT_EXEC) } != 0 {
|
if unsafe { mprotect(self.ptr as _, self.size, PROT_READ | PROT_EXEC) } != 0 {
|
||||||
panic!("cannot set code memory to executable");
|
panic!("cannot set code memory to executable");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Makes this code memory writable.
|
/// Makes this code memory writable and not executable.
|
||||||
pub fn make_writable(&self) {
|
pub fn make_writable(&self) {
|
||||||
if unsafe { mprotect(self.ptr as _, self.size, PROT_READ | PROT_WRITE) } != 0 {
|
if unsafe { mprotect(self.ptr as _, self.size, PROT_READ | PROT_WRITE) } != 0 {
|
||||||
panic!("cannot set code memory to writable");
|
panic!("cannot set code memory to writable");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Makes this code memory both writable and executable.
|
||||||
|
///
|
||||||
|
/// Avoid using this if a combination `make_executable` and `make_writable` can be used.
|
||||||
|
pub fn make_writable_executable(&self) {
|
||||||
|
if unsafe { mprotect(self.ptr as _, self.size, PROT_READ | PROT_WRITE | PROT_EXEC) } != 0 {
|
||||||
|
panic!("cannot set code memory to writable and executable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the backing pointer of this code memory.
|
||||||
|
pub fn get_backing_ptr(&self) -> *mut u8 {
|
||||||
|
self.ptr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! A reusable pointer abstraction for getting memory from the guest's memory.
|
//! Types for a reusable pointer abstraction for accessing Wasm linear memory.
|
||||||
//!
|
//!
|
||||||
//! This abstraction is safe: it ensures the memory is in bounds and that the pointer
|
//! This abstraction is safe: it ensures the memory is in bounds and that the pointer
|
||||||
//! is aligned (avoiding undefined behavior).
|
//! is aligned (avoiding undefined behavior).
|
||||||
@ -12,18 +12,36 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use std::{cell::Cell, fmt, marker::PhantomData, mem};
|
use std::{cell::Cell, fmt, marker::PhantomData, mem};
|
||||||
|
|
||||||
/// Array.
|
/// The `Array` marker type. This type can be used like `WasmPtr<T, Array>`
|
||||||
|
/// to get access to methods
|
||||||
pub struct Array;
|
pub struct Array;
|
||||||
/// Item.
|
/// The `Item` marker type. This is the default and does not usually need to be
|
||||||
|
/// specified.
|
||||||
pub struct Item;
|
pub struct Item;
|
||||||
|
|
||||||
/// A pointer to a Wasm item.
|
/// A zero-cost type that represents a pointer to something in Wasm linear
|
||||||
|
/// memory.
|
||||||
|
///
|
||||||
|
/// This type can be used directly in the host function arguments:
|
||||||
|
/// ```
|
||||||
|
/// # use wasmer_runtime_core::vm::Ctx;
|
||||||
|
/// # use wasmer_runtime_core::memory::ptr::WasmPtr;
|
||||||
|
/// pub fn host_import(ctx: &mut Ctx, ptr: WasmPtr<u32>) {
|
||||||
|
/// let memory = ctx.memory(0);
|
||||||
|
/// let derefed_ptr = ptr.deref(memory).expect("pointer in bounds");
|
||||||
|
/// let inner_val: u32 = derefed_ptr.get();
|
||||||
|
/// println!("Got {} from Wasm memory address 0x{:X}", inner_val, ptr.offset());
|
||||||
|
/// // update the value being pointed to
|
||||||
|
/// derefed_ptr.set(inner_val + 1);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct WasmPtr<T: Copy, Ty = Item> {
|
pub struct WasmPtr<T: Copy, Ty = Item> {
|
||||||
offset: u32,
|
offset: u32,
|
||||||
_phantom: PhantomData<(T, Ty)>,
|
_phantom: PhantomData<(T, Ty)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Methods relevant to all types of `WasmPtr`.
|
||||||
impl<T: Copy, Ty> WasmPtr<T, Ty> {
|
impl<T: Copy, Ty> WasmPtr<T, Ty> {
|
||||||
/// Create a new `WasmPtr` at the given offset.
|
/// Create a new `WasmPtr` at the given offset.
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -34,7 +52,7 @@ impl<T: Copy, Ty> WasmPtr<T, Ty> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the offset for this `WasmPtr`.
|
/// Get the offset into Wasm linear memory for this `WasmPtr`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn offset(self) -> u32 {
|
pub fn offset(self) -> u32 {
|
||||||
self.offset
|
self.offset
|
||||||
@ -48,11 +66,21 @@ fn align_pointer(ptr: usize, align: usize) -> usize {
|
|||||||
ptr & !(align - 1)
|
ptr & !(align - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Methods for `WasmPtr`s to data that can be dereferenced, namely to types
|
||||||
|
/// that implement [`ValueType`], meaning that they're valid for all possible
|
||||||
|
/// bit patterns.
|
||||||
impl<T: Copy + ValueType> WasmPtr<T, Item> {
|
impl<T: Copy + ValueType> WasmPtr<T, Item> {
|
||||||
/// Dereference this `WasmPtr`.
|
/// Dereference the `WasmPtr` getting access to a `&Cell<T>` allowing for
|
||||||
|
/// reading and mutating of the inner value.
|
||||||
|
///
|
||||||
|
/// This method is unsound if used with unsynchronized shared memory.
|
||||||
|
/// If you're unsure what that means, it likely does not apply to you.
|
||||||
|
/// This invariant will be enforced in the future.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn deref<'a>(self, memory: &'a Memory) -> Option<&'a Cell<T>> {
|
pub fn deref<'a>(self, memory: &'a Memory) -> Option<&'a Cell<T>> {
|
||||||
if (self.offset as usize) + mem::size_of::<T>() >= memory.size().bytes().0 {
|
if (self.offset as usize) + mem::size_of::<T>() > memory.size().bytes().0
|
||||||
|
|| mem::size_of::<T>() == 0
|
||||||
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -64,10 +92,18 @@ impl<T: Copy + ValueType> WasmPtr<T, Item> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mutable dereference this `WasmPtr`.
|
/// Mutably dereference this `WasmPtr` getting a `&mut Cell<T>` allowing for
|
||||||
|
/// direct access to a `&mut T`.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// - This method does not do any aliasing checks: it's possible to create
|
||||||
|
/// `&mut T` that point to the same memory. You should ensure that you have
|
||||||
|
/// exclusive access to Wasm linear memory before calling this method.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn deref_mut<'a>(self, memory: &'a Memory) -> Option<&'a mut Cell<T>> {
|
pub unsafe fn deref_mut<'a>(self, memory: &'a Memory) -> Option<&'a mut Cell<T>> {
|
||||||
if (self.offset as usize) + mem::size_of::<T>() >= memory.size().bytes().0 {
|
if (self.offset as usize) + mem::size_of::<T>() > memory.size().bytes().0
|
||||||
|
|| mem::size_of::<T>() == 0
|
||||||
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let cell_ptr = align_pointer(
|
let cell_ptr = align_pointer(
|
||||||
@ -78,16 +114,28 @@ impl<T: Copy + ValueType> WasmPtr<T, Item> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Methods for `WasmPtr`s to arrays of data that can be dereferenced, namely to
|
||||||
|
/// types that implement [`ValueType`], meaning that they're valid for all
|
||||||
|
/// possible bit patterns.
|
||||||
impl<T: Copy + ValueType> WasmPtr<T, Array> {
|
impl<T: Copy + ValueType> WasmPtr<T, Array> {
|
||||||
/// Dereference this `WasmPtr`.
|
/// Dereference the `WasmPtr` getting access to a `&[Cell<T>]` allowing for
|
||||||
|
/// reading and mutating of the inner values.
|
||||||
|
///
|
||||||
|
/// This method is unsound if used with unsynchronized shared memory.
|
||||||
|
/// If you're unsure what that means, it likely does not apply to you.
|
||||||
|
/// This invariant will be enforced in the future.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn deref(self, memory: &Memory, index: u32, length: u32) -> Option<&[Cell<T>]> {
|
pub fn deref(self, memory: &Memory, index: u32, length: u32) -> Option<&[Cell<T>]> {
|
||||||
// gets the size of the item in the array with padding added such that
|
// gets the size of the item in the array with padding added such that
|
||||||
// for any index, we will always result an aligned memory access
|
// for any index, we will always result an aligned memory access
|
||||||
let item_size = mem::size_of::<T>() + (mem::size_of::<T>() % mem::align_of::<T>());
|
let item_size = mem::size_of::<T>() + (mem::size_of::<T>() % mem::align_of::<T>());
|
||||||
let slice_full_len = index as usize + length as usize;
|
let slice_full_len = index as usize + length as usize;
|
||||||
|
let memory_size = memory.size().bytes().0;
|
||||||
|
|
||||||
if (self.offset as usize) + (item_size * slice_full_len) >= memory.size().bytes().0 {
|
if (self.offset as usize) + (item_size * slice_full_len) > memory_size
|
||||||
|
|| self.offset as usize >= memory_size
|
||||||
|
|| mem::size_of::<T>() == 0
|
||||||
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +150,13 @@ impl<T: Copy + ValueType> WasmPtr<T, Array> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mutable dereference this `WasmPtr`.
|
/// Mutably dereference this `WasmPtr` getting a `&mut [Cell<T>]` allowing for
|
||||||
|
/// direct access to a `&mut [T]`.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// - This method does not do any aliasing checks: it's possible to create
|
||||||
|
/// `&mut T` that point to the same memory. You should ensure that you have
|
||||||
|
/// exclusive access to Wasm linear memory before calling this method.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn deref_mut(
|
pub unsafe fn deref_mut(
|
||||||
self,
|
self,
|
||||||
@ -114,8 +168,12 @@ impl<T: Copy + ValueType> WasmPtr<T, Array> {
|
|||||||
// for any index, we will always result an aligned memory access
|
// for any index, we will always result an aligned memory access
|
||||||
let item_size = mem::size_of::<T>() + (mem::size_of::<T>() % mem::align_of::<T>());
|
let item_size = mem::size_of::<T>() + (mem::size_of::<T>() % mem::align_of::<T>());
|
||||||
let slice_full_len = index as usize + length as usize;
|
let slice_full_len = index as usize + length as usize;
|
||||||
|
let memory_size = memory.size().bytes().0;
|
||||||
|
|
||||||
if (self.offset as usize) + (item_size * slice_full_len) >= memory.size().bytes().0 {
|
if (self.offset as usize) + (item_size * slice_full_len) > memory.size().bytes().0
|
||||||
|
|| self.offset as usize >= memory_size
|
||||||
|
|| mem::size_of::<T>() == 0
|
||||||
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,9 +186,17 @@ impl<T: Copy + ValueType> WasmPtr<T, Array> {
|
|||||||
Some(cell_ptrs)
|
Some(cell_ptrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a UTF-8 string representation of this `WasmPtr` with the given length.
|
/// Get a UTF-8 string from the `WasmPtr` with the given length.
|
||||||
|
///
|
||||||
|
/// Note that this method returns a reference to Wasm linear memory. The
|
||||||
|
/// underlying data can be mutated if the Wasm is allowed to execute or
|
||||||
|
/// an aliasing `WasmPtr` is used to mutate memory.
|
||||||
pub fn get_utf8_string(self, memory: &Memory, str_len: u32) -> Option<&str> {
|
pub fn get_utf8_string(self, memory: &Memory, str_len: u32) -> Option<&str> {
|
||||||
if self.offset as usize + str_len as usize > memory.size().bytes().0 {
|
let memory_size = memory.size().bytes().0;
|
||||||
|
|
||||||
|
if self.offset as usize + str_len as usize > memory.size().bytes().0
|
||||||
|
|| self.offset as usize >= memory_size
|
||||||
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let ptr = unsafe { memory.view::<u8>().as_ptr().add(self.offset as usize) as *const u8 };
|
let ptr = unsafe { memory.view::<u8>().as_ptr().add(self.offset as usize) as *const u8 };
|
||||||
@ -138,9 +204,14 @@ impl<T: Copy + ValueType> WasmPtr<T, Array> {
|
|||||||
std::str::from_utf8(slice).ok()
|
std::str::from_utf8(slice).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a UTF-8 string representation of this `WasmPtr`, where the string is nul-terminated.
|
/// Get a UTF-8 string from the `WasmPtr`, where the string is nul-terminated.
|
||||||
|
///
|
||||||
/// Note that this does not account for UTF-8 strings that _contain_ nul themselves,
|
/// Note that this does not account for UTF-8 strings that _contain_ nul themselves,
|
||||||
/// [`get_utf8_string`] has to be used for those.
|
/// [`get_utf8_string`] has to be used for those.
|
||||||
|
///
|
||||||
|
/// Also note that this method returns a reference to Wasm linear memory. The
|
||||||
|
/// underlying data can be mutated if the Wasm is allowed to execute or
|
||||||
|
/// an aliasing `WasmPtr` is used to mutate memory.
|
||||||
pub fn get_utf8_string_with_nul(self, memory: &Memory) -> Option<&str> {
|
pub fn get_utf8_string_with_nul(self, memory: &Memory) -> Option<&str> {
|
||||||
memory.view::<u8>()[(self.offset as usize)..]
|
memory.view::<u8>()[(self.offset as usize)..]
|
||||||
.iter()
|
.iter()
|
||||||
@ -190,3 +261,94 @@ impl<T: Copy, Ty> fmt::Debug for WasmPtr<T, Ty> {
|
|||||||
write!(f, "WasmPtr({:#x})", self.offset)
|
write!(f, "WasmPtr({:#x})", self.offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::memory;
|
||||||
|
use crate::units::Pages;
|
||||||
|
|
||||||
|
/// Ensure that memory accesses work on the edges of memory and that out of
|
||||||
|
/// bounds errors are caught with both `deref` and `deref_mut`.
|
||||||
|
#[test]
|
||||||
|
fn wasm_ptr_memory_bounds_checks_hold() {
|
||||||
|
// create a memory
|
||||||
|
let memory_descriptor =
|
||||||
|
memory::MemoryDescriptor::new(Pages(1), Some(Pages(1)), false).unwrap();
|
||||||
|
let memory = memory::Memory::new(memory_descriptor).unwrap();
|
||||||
|
|
||||||
|
// test that basic access works and that len = 0 works, but oob does not
|
||||||
|
let start_wasm_ptr: WasmPtr<u8> = WasmPtr::new(0);
|
||||||
|
let start_wasm_ptr_array: WasmPtr<u8, Array> = WasmPtr::new(0);
|
||||||
|
|
||||||
|
assert!(start_wasm_ptr.deref(&memory).is_some());
|
||||||
|
assert!(unsafe { start_wasm_ptr.deref_mut(&memory).is_some() });
|
||||||
|
assert!(start_wasm_ptr_array.deref(&memory, 0, 0).is_some());
|
||||||
|
assert!(start_wasm_ptr_array.get_utf8_string(&memory, 0).is_some());
|
||||||
|
assert!(unsafe { start_wasm_ptr_array.deref_mut(&memory, 0, 0).is_some() });
|
||||||
|
assert!(start_wasm_ptr_array.deref(&memory, 0, 1).is_some());
|
||||||
|
assert!(unsafe { start_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() });
|
||||||
|
|
||||||
|
// test that accessing the last valid memory address works correctly and OOB is caught
|
||||||
|
let last_valid_address_for_u8 = (memory.size().bytes().0 - 1) as u32;
|
||||||
|
let end_wasm_ptr: WasmPtr<u8> = WasmPtr::new(last_valid_address_for_u8);
|
||||||
|
assert!(end_wasm_ptr.deref(&memory).is_some());
|
||||||
|
assert!(unsafe { end_wasm_ptr.deref_mut(&memory).is_some() });
|
||||||
|
|
||||||
|
let end_wasm_ptr_array: WasmPtr<u8, Array> = WasmPtr::new(last_valid_address_for_u8);
|
||||||
|
|
||||||
|
assert!(end_wasm_ptr_array.deref(&memory, 0, 1).is_some());
|
||||||
|
assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() });
|
||||||
|
let invalid_idx_len_combos: [(u32, u32); 3] =
|
||||||
|
[(last_valid_address_for_u8 + 1, 0), (0, 2), (1, 1)];
|
||||||
|
for &(idx, len) in invalid_idx_len_combos.into_iter() {
|
||||||
|
assert!(end_wasm_ptr_array.deref(&memory, idx, len).is_none());
|
||||||
|
assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, idx, len).is_none() });
|
||||||
|
}
|
||||||
|
assert!(end_wasm_ptr_array.get_utf8_string(&memory, 2).is_none());
|
||||||
|
|
||||||
|
// test that accesing the last valid memory address for a u32 is valid
|
||||||
|
// (same as above test but with more edge cases to assert on)
|
||||||
|
let last_valid_address_for_u32 = (memory.size().bytes().0 - 4) as u32;
|
||||||
|
let end_wasm_ptr: WasmPtr<u32> = WasmPtr::new(last_valid_address_for_u32);
|
||||||
|
assert!(end_wasm_ptr.deref(&memory).is_some());
|
||||||
|
assert!(unsafe { end_wasm_ptr.deref_mut(&memory).is_some() });
|
||||||
|
assert!(end_wasm_ptr.deref(&memory).is_some());
|
||||||
|
assert!(unsafe { end_wasm_ptr.deref_mut(&memory).is_some() });
|
||||||
|
|
||||||
|
let end_wasm_ptr_oob_array: [WasmPtr<u32>; 4] = [
|
||||||
|
WasmPtr::new(last_valid_address_for_u32 + 1),
|
||||||
|
WasmPtr::new(last_valid_address_for_u32 + 2),
|
||||||
|
WasmPtr::new(last_valid_address_for_u32 + 3),
|
||||||
|
WasmPtr::new(last_valid_address_for_u32 + 4),
|
||||||
|
];
|
||||||
|
for oob_end_ptr in end_wasm_ptr_oob_array.into_iter() {
|
||||||
|
assert!(oob_end_ptr.deref(&memory).is_none());
|
||||||
|
assert!(unsafe { oob_end_ptr.deref_mut(&memory).is_none() });
|
||||||
|
}
|
||||||
|
let end_wasm_ptr_array: WasmPtr<u32, Array> = WasmPtr::new(last_valid_address_for_u32);
|
||||||
|
assert!(end_wasm_ptr_array.deref(&memory, 0, 1).is_some());
|
||||||
|
assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() });
|
||||||
|
|
||||||
|
let invalid_idx_len_combos: [(u32, u32); 3] =
|
||||||
|
[(last_valid_address_for_u32 + 1, 0), (0, 2), (1, 1)];
|
||||||
|
for &(idx, len) in invalid_idx_len_combos.into_iter() {
|
||||||
|
assert!(end_wasm_ptr_array.deref(&memory, idx, len).is_none());
|
||||||
|
assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, idx, len).is_none() });
|
||||||
|
}
|
||||||
|
|
||||||
|
let end_wasm_ptr_array_oob_array: [WasmPtr<u32, Array>; 4] = [
|
||||||
|
WasmPtr::new(last_valid_address_for_u32 + 1),
|
||||||
|
WasmPtr::new(last_valid_address_for_u32 + 2),
|
||||||
|
WasmPtr::new(last_valid_address_for_u32 + 3),
|
||||||
|
WasmPtr::new(last_valid_address_for_u32 + 4),
|
||||||
|
];
|
||||||
|
|
||||||
|
for oob_end_array_ptr in end_wasm_ptr_array_oob_array.into_iter() {
|
||||||
|
assert!(oob_end_array_ptr.deref(&memory, 0, 1).is_none());
|
||||||
|
assert!(unsafe { oob_end_array_ptr.deref_mut(&memory, 0, 1).is_none() });
|
||||||
|
assert!(oob_end_array_ptr.deref(&memory, 1, 0).is_none());
|
||||||
|
assert!(unsafe { oob_end_array_ptr.deref_mut(&memory, 1, 0).is_none() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,8 +6,8 @@ use crate::{
|
|||||||
backend::{CompilerConfig, RunnableModule},
|
backend::{CompilerConfig, RunnableModule},
|
||||||
error::CompileError,
|
error::CompileError,
|
||||||
module::{
|
module::{
|
||||||
DataInitializer, ExportIndex, ImportName, ModuleInfo, StringTable, StringTableBuilder,
|
DataInitializer, ExportIndex, ImportName, ModuleInfo, NameIndex, NamespaceIndex,
|
||||||
TableInitializer,
|
StringTable, StringTableBuilder, TableInitializer,
|
||||||
},
|
},
|
||||||
structures::{Map, TypedIndex},
|
structures::{Map, TypedIndex},
|
||||||
types::{
|
types::{
|
||||||
@ -110,11 +110,36 @@ pub fn read_module<
|
|||||||
let mut namespace_builder = Some(StringTableBuilder::new());
|
let mut namespace_builder = Some(StringTableBuilder::new());
|
||||||
let mut name_builder = Some(StringTableBuilder::new());
|
let mut name_builder = Some(StringTableBuilder::new());
|
||||||
let mut func_count: usize = 0;
|
let mut func_count: usize = 0;
|
||||||
let mut mcg_info_fed = false;
|
|
||||||
|
let mut feed_mcg_signatures: Option<_> = Some(|mcg: &mut MCG| -> Result<(), LoadError> {
|
||||||
|
let info_read = info.read().unwrap();
|
||||||
|
mcg.feed_signatures(info_read.signatures.clone())
|
||||||
|
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
let mut feed_mcg_info: Option<_> = Some(
|
||||||
|
|mcg: &mut MCG,
|
||||||
|
ns_builder: StringTableBuilder<NamespaceIndex>,
|
||||||
|
name_builder: StringTableBuilder<NameIndex>|
|
||||||
|
-> Result<(), LoadError> {
|
||||||
|
{
|
||||||
|
let mut info_write = info.write().unwrap();
|
||||||
|
info_write.namespace_table = ns_builder.finish();
|
||||||
|
info_write.name_table = name_builder.finish();
|
||||||
|
}
|
||||||
|
let info_read = info.read().unwrap();
|
||||||
|
mcg.feed_function_signatures(info_read.func_assoc.clone())
|
||||||
|
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
|
||||||
|
mcg.check_precondition(&info_read)
|
||||||
|
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
use wasmparser::ParserState;
|
use wasmparser::ParserState;
|
||||||
let state = parser.read();
|
let state = parser.read();
|
||||||
|
|
||||||
match *state {
|
match *state {
|
||||||
ParserState::Error(ref err) => return Err(err.clone().into()),
|
ParserState::Error(ref err) => return Err(err.clone().into()),
|
||||||
ParserState::TypeSectionEntry(ref ty) => {
|
ParserState::TypeSectionEntry(ref ty) => {
|
||||||
@ -124,6 +149,10 @@ pub fn read_module<
|
|||||||
.push(func_type_to_func_sig(ty)?);
|
.push(func_type_to_func_sig(ty)?);
|
||||||
}
|
}
|
||||||
ParserState::ImportSectionEntry { module, field, ty } => {
|
ParserState::ImportSectionEntry { module, field, ty } => {
|
||||||
|
if let Some(f) = feed_mcg_signatures.take() {
|
||||||
|
f(mcg)?;
|
||||||
|
}
|
||||||
|
|
||||||
let namespace_index = namespace_builder.as_mut().unwrap().register(module);
|
let namespace_index = namespace_builder.as_mut().unwrap().register(module);
|
||||||
let name_index = name_builder.as_mut().unwrap().register(field);
|
let name_index = name_builder.as_mut().unwrap().register(field);
|
||||||
let import_name = ImportName {
|
let import_name = ImportName {
|
||||||
@ -136,7 +165,7 @@ pub fn read_module<
|
|||||||
let sigindex = SigIndex::new(sigindex as usize);
|
let sigindex = SigIndex::new(sigindex as usize);
|
||||||
info.write().unwrap().imported_functions.push(import_name);
|
info.write().unwrap().imported_functions.push(import_name);
|
||||||
info.write().unwrap().func_assoc.push(sigindex);
|
info.write().unwrap().func_assoc.push(sigindex);
|
||||||
mcg.feed_import_function()
|
mcg.feed_import_function(sigindex)
|
||||||
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
|
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
|
||||||
}
|
}
|
||||||
ImportSectionEntryType::Table(table_ty) => {
|
ImportSectionEntryType::Table(table_ty) => {
|
||||||
@ -217,23 +246,17 @@ pub fn read_module<
|
|||||||
info.write().unwrap().start_func = Some(FuncIndex::new(start_index as usize));
|
info.write().unwrap().start_func = Some(FuncIndex::new(start_index as usize));
|
||||||
}
|
}
|
||||||
ParserState::BeginFunctionBody { range } => {
|
ParserState::BeginFunctionBody { range } => {
|
||||||
|
if let Some(f) = feed_mcg_signatures.take() {
|
||||||
|
f(mcg)?;
|
||||||
|
}
|
||||||
|
if let Some(f) = feed_mcg_info.take() {
|
||||||
|
f(
|
||||||
|
mcg,
|
||||||
|
namespace_builder.take().unwrap(),
|
||||||
|
name_builder.take().unwrap(),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
let id = func_count;
|
let id = func_count;
|
||||||
if !mcg_info_fed {
|
|
||||||
mcg_info_fed = true;
|
|
||||||
{
|
|
||||||
let mut info_write = info.write().unwrap();
|
|
||||||
info_write.namespace_table = namespace_builder.take().unwrap().finish();
|
|
||||||
info_write.name_table = name_builder.take().unwrap().finish();
|
|
||||||
}
|
|
||||||
let info_read = info.read().unwrap();
|
|
||||||
mcg.feed_signatures(info_read.signatures.clone())
|
|
||||||
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
|
|
||||||
mcg.feed_function_signatures(info_read.func_assoc.clone())
|
|
||||||
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
|
|
||||||
mcg.check_precondition(&info_read)
|
|
||||||
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let fcg = mcg
|
let fcg = mcg
|
||||||
.next_function(
|
.next_function(
|
||||||
Arc::clone(&info),
|
Arc::clone(&info),
|
||||||
@ -432,17 +455,15 @@ pub fn read_module<
|
|||||||
info.write().unwrap().globals.push(global_init);
|
info.write().unwrap().globals.push(global_init);
|
||||||
}
|
}
|
||||||
ParserState::EndWasm => {
|
ParserState::EndWasm => {
|
||||||
// TODO Consolidate with BeginFunction body if possible
|
if let Some(f) = feed_mcg_signatures.take() {
|
||||||
if !mcg_info_fed {
|
f(mcg)?;
|
||||||
info.write().unwrap().namespace_table =
|
}
|
||||||
namespace_builder.take().unwrap().finish();
|
if let Some(f) = feed_mcg_info.take() {
|
||||||
info.write().unwrap().name_table = name_builder.take().unwrap().finish();
|
f(
|
||||||
mcg.feed_signatures(info.read().unwrap().signatures.clone())
|
mcg,
|
||||||
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
|
namespace_builder.take().unwrap(),
|
||||||
mcg.feed_function_signatures(info.read().unwrap().func_assoc.clone())
|
name_builder.take().unwrap(),
|
||||||
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
|
)?;
|
||||||
mcg.check_precondition(&info.read().unwrap())
|
|
||||||
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -480,10 +480,11 @@ impl InstanceImage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Declarations for x86-64 registers.
|
/// X64-specific structures and methods that do not depend on an x64 machine to run.
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
pub mod x64_decl {
|
pub mod x64_decl {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::types::Type;
|
||||||
|
|
||||||
/// General-purpose registers.
|
/// General-purpose registers.
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
@ -610,9 +611,88 @@ pub mod x64_decl {
|
|||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the instruction prefix for `movq %this_reg, ?(%rsp)`.
|
||||||
|
///
|
||||||
|
/// To build an instruction, append the memory location as a 32-bit
|
||||||
|
/// offset to the stack pointer to this prefix.
|
||||||
|
pub fn prefix_mov_to_stack(&self) -> Option<&'static [u8]> {
|
||||||
|
Some(match *self {
|
||||||
|
X64Register::GPR(gpr) => match gpr {
|
||||||
|
GPR::RDI => &[0x48, 0x89, 0xbc, 0x24],
|
||||||
|
GPR::RSI => &[0x48, 0x89, 0xb4, 0x24],
|
||||||
|
GPR::RDX => &[0x48, 0x89, 0x94, 0x24],
|
||||||
|
GPR::RCX => &[0x48, 0x89, 0x8c, 0x24],
|
||||||
|
GPR::R8 => &[0x4c, 0x89, 0x84, 0x24],
|
||||||
|
GPR::R9 => &[0x4c, 0x89, 0x8c, 0x24],
|
||||||
|
_ => return None,
|
||||||
|
},
|
||||||
|
X64Register::XMM(xmm) => match xmm {
|
||||||
|
XMM::XMM0 => &[0x66, 0x0f, 0xd6, 0x84, 0x24],
|
||||||
|
XMM::XMM1 => &[0x66, 0x0f, 0xd6, 0x8c, 0x24],
|
||||||
|
XMM::XMM2 => &[0x66, 0x0f, 0xd6, 0x94, 0x24],
|
||||||
|
XMM::XMM3 => &[0x66, 0x0f, 0xd6, 0x9c, 0x24],
|
||||||
|
XMM::XMM4 => &[0x66, 0x0f, 0xd6, 0xa4, 0x24],
|
||||||
|
XMM::XMM5 => &[0x66, 0x0f, 0xd6, 0xac, 0x24],
|
||||||
|
XMM::XMM6 => &[0x66, 0x0f, 0xd6, 0xb4, 0x24],
|
||||||
|
XMM::XMM7 => &[0x66, 0x0f, 0xd6, 0xbc, 0x24],
|
||||||
|
_ => return None,
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An allocator that allocates registers for function arguments according to the System V ABI.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ArgumentRegisterAllocator {
|
||||||
|
n_gprs: usize,
|
||||||
|
n_xmms: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArgumentRegisterAllocator {
|
||||||
|
/// Allocates a register for argument type `ty`. Returns `None` if no register is available for this type.
|
||||||
|
pub fn next(&mut self, ty: Type) -> Option<X64Register> {
|
||||||
|
static GPR_SEQ: &'static [GPR] =
|
||||||
|
&[GPR::RDI, GPR::RSI, GPR::RDX, GPR::RCX, GPR::R8, GPR::R9];
|
||||||
|
static XMM_SEQ: &'static [XMM] = &[
|
||||||
|
XMM::XMM0,
|
||||||
|
XMM::XMM1,
|
||||||
|
XMM::XMM2,
|
||||||
|
XMM::XMM3,
|
||||||
|
XMM::XMM4,
|
||||||
|
XMM::XMM5,
|
||||||
|
XMM::XMM6,
|
||||||
|
XMM::XMM7,
|
||||||
|
];
|
||||||
|
match ty {
|
||||||
|
Type::I32 | Type::I64 => {
|
||||||
|
if self.n_gprs < GPR_SEQ.len() {
|
||||||
|
let gpr = GPR_SEQ[self.n_gprs];
|
||||||
|
self.n_gprs += 1;
|
||||||
|
Some(X64Register::GPR(gpr))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::F32 | Type::F64 => {
|
||||||
|
if self.n_xmms < XMM_SEQ.len() {
|
||||||
|
let xmm = XMM_SEQ[self.n_xmms];
|
||||||
|
self.n_xmms += 1;
|
||||||
|
Some(X64Register::XMM(xmm))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => todo!(
|
||||||
|
"ArgumentRegisterAllocator::next: Unsupported type: {:?}",
|
||||||
|
ty
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// X64-specific structures and methods that only work on an x64 machine.
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
pub mod x64 {
|
pub mod x64 {
|
||||||
//! The x64 state module contains functions to generate state and code for x64 targets.
|
//! The x64 state module contains functions to generate state and code for x64 targets.
|
||||||
|
@ -7,8 +7,13 @@
|
|||||||
//! Variadic functions are not supported because `rax` is used by the trampoline code.
|
//! Variadic functions are not supported because `rax` is used by the trampoline code.
|
||||||
|
|
||||||
use crate::loader::CodeMemory;
|
use crate::loader::CodeMemory;
|
||||||
|
use crate::state::x64_decl::ArgumentRegisterAllocator;
|
||||||
|
use crate::types::Type;
|
||||||
use crate::vm::Ctx;
|
use crate::vm::Ctx;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::ptr::NonNull;
|
||||||
|
use std::sync::Mutex;
|
||||||
use std::{mem, slice};
|
use std::{mem, slice};
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
@ -29,6 +34,96 @@ lazy_static! {
|
|||||||
mem::transmute(ptr)
|
mem::transmute(ptr)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static ref TRAMPOLINES: TrampBuffer = TrampBuffer::new(64 * 1048576);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The global trampoline buffer.
|
||||||
|
struct TrampBuffer {
|
||||||
|
/// A fixed-(virtual)-size executable+writable buffer for storing trampolines.
|
||||||
|
buffer: CodeMemory,
|
||||||
|
|
||||||
|
/// Allocation state.
|
||||||
|
alloc: Mutex<AllocState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The allocation state of a `TrampBuffer`.
|
||||||
|
struct AllocState {
|
||||||
|
/// Records all allocated blocks in `buffer`.
|
||||||
|
///
|
||||||
|
/// Maps the start address of each block to its end address.
|
||||||
|
blocks: BTreeMap<usize, usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TrampBuffer {
|
||||||
|
/// Creates a trampoline buffer with a given (virtual) size.
|
||||||
|
fn new(size: usize) -> TrampBuffer {
|
||||||
|
let mem = CodeMemory::new(size);
|
||||||
|
mem.make_writable_executable();
|
||||||
|
TrampBuffer {
|
||||||
|
buffer: mem,
|
||||||
|
alloc: Mutex::new(AllocState {
|
||||||
|
blocks: BTreeMap::new(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes a previously-`insert`ed trampoline.
|
||||||
|
///
|
||||||
|
/// For safety, refer to the public interface `TrampolineBufferBuilder::remove_global`.
|
||||||
|
unsafe fn remove(&self, start: NonNull<u8>) {
|
||||||
|
let start = start.as_ptr() as usize - self.buffer.get_backing_ptr() as usize;
|
||||||
|
let mut alloc = self.alloc.lock().unwrap();
|
||||||
|
alloc
|
||||||
|
.blocks
|
||||||
|
.remove(&start)
|
||||||
|
.expect("TrampBuffer::remove(): Attempting to remove a non-existent allocation.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocates a region of executable memory and copies `buf` to the end of this region.
|
||||||
|
///
|
||||||
|
/// Returns `None` if no memory is available.
|
||||||
|
fn insert(&self, buf: &[u8]) -> Option<NonNull<u8>> {
|
||||||
|
// First, assume an available start position...
|
||||||
|
let mut assumed_start: usize = 0;
|
||||||
|
|
||||||
|
let mut alloc = self.alloc.lock().unwrap();
|
||||||
|
let mut found = false;
|
||||||
|
|
||||||
|
// Then, try invalidating that assumption...
|
||||||
|
for (&start, &end) in &alloc.blocks {
|
||||||
|
if start - assumed_start < buf.len() {
|
||||||
|
// Unavailable. Move to next free block.
|
||||||
|
assumed_start = end;
|
||||||
|
} else {
|
||||||
|
// This free block can be used.
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
// No previous free blocks were found. Try allocating at the end.
|
||||||
|
if self.buffer.len() - assumed_start < buf.len() {
|
||||||
|
// No more free space. Cannot allocate.
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we know `assumed_start` is valid.
|
||||||
|
let start = assumed_start;
|
||||||
|
alloc.blocks.insert(start, start + buf.len());
|
||||||
|
|
||||||
|
// We have unique ownership to `self.buffer[start..start + buf.len()]`.
|
||||||
|
let slice = unsafe {
|
||||||
|
std::slice::from_raw_parts_mut(
|
||||||
|
self.buffer.get_backing_ptr().offset(start as _),
|
||||||
|
buf.len(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
slice.copy_from_slice(buf);
|
||||||
|
Some(NonNull::new(slice.as_mut_ptr()).unwrap())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An opaque type for pointers to a callable memory location.
|
/// An opaque type for pointers to a callable memory location.
|
||||||
@ -153,44 +248,50 @@ impl TrampolineBufferBuilder {
|
|||||||
&mut self,
|
&mut self,
|
||||||
target: unsafe extern "C" fn(*const CallContext, *const u64) -> u64,
|
target: unsafe extern "C" fn(*const CallContext, *const u64) -> u64,
|
||||||
context: *const CallContext,
|
context: *const CallContext,
|
||||||
num_params: u32,
|
params: &[Type],
|
||||||
|
_returns: &[Type],
|
||||||
) -> usize {
|
) -> usize {
|
||||||
let idx = self.offsets.len();
|
let idx = self.offsets.len();
|
||||||
self.offsets.push(self.code.len());
|
self.offsets.push(self.code.len());
|
||||||
|
|
||||||
let mut stack_offset: u32 = num_params.checked_mul(8).unwrap();
|
let mut stack_offset: u32 = params.len().checked_mul(8).unwrap() as u32;
|
||||||
if stack_offset % 16 == 0 {
|
if stack_offset % 16 == 0 {
|
||||||
stack_offset += 8;
|
stack_offset += 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.code.extend_from_slice(&[0x48, 0x81, 0xec]); // sub ?, %rsp
|
self.code.extend_from_slice(&[0x48, 0x81, 0xec]); // sub ?, %rsp
|
||||||
self.code.extend_from_slice(value_to_bytes(&stack_offset));
|
self.code.extend_from_slice(value_to_bytes(&stack_offset));
|
||||||
for i in 0..num_params {
|
|
||||||
match i {
|
let mut allocator = ArgumentRegisterAllocator::default();
|
||||||
0..=5 => {
|
|
||||||
// mov %?, ?(%rsp)
|
let mut source_stack_count: u32 = 0; // # of allocated slots in the source stack.
|
||||||
let prefix: &[u8] = match i {
|
|
||||||
0 => &[0x48, 0x89, 0xbc, 0x24], // rdi
|
for (i, ty) in params.iter().enumerate() {
|
||||||
1 => &[0x48, 0x89, 0xb4, 0x24], // rsi
|
match allocator.next(*ty) {
|
||||||
2 => &[0x48, 0x89, 0x94, 0x24], // rdx
|
Some(reg) => {
|
||||||
3 => &[0x48, 0x89, 0x8c, 0x24], // rcx
|
// This argument is allocated to a register.
|
||||||
4 => &[0x4c, 0x89, 0x84, 0x24], // r8
|
|
||||||
5 => &[0x4c, 0x89, 0x8c, 0x24], // r9
|
let prefix = reg
|
||||||
_ => unreachable!(),
|
.prefix_mov_to_stack()
|
||||||
};
|
.expect("cannot get instruction prefix for argument register");
|
||||||
self.code.extend_from_slice(prefix);
|
self.code.extend_from_slice(prefix);
|
||||||
self.code.extend_from_slice(value_to_bytes(&(i * 8u32)));
|
self.code
|
||||||
|
.extend_from_slice(value_to_bytes(&((i as u32) * 8u32)));
|
||||||
}
|
}
|
||||||
_ => {
|
None => {
|
||||||
|
// This argument is allocated to the stack.
|
||||||
|
|
||||||
self.code.extend_from_slice(&[
|
self.code.extend_from_slice(&[
|
||||||
0x48, 0x8b, 0x84, 0x24, // mov ?(%rsp), %rax
|
0x48, 0x8b, 0x84, 0x24, // mov ?(%rsp), %rax
|
||||||
]);
|
]);
|
||||||
self.code.extend_from_slice(value_to_bytes(
|
self.code.extend_from_slice(value_to_bytes(
|
||||||
&((i - 6) * 8u32 + stack_offset + 8/* ret addr */),
|
&(source_stack_count * 8u32 + stack_offset + 8/* ret addr */),
|
||||||
));
|
));
|
||||||
// mov %rax, ?(%rsp)
|
// mov %rax, ?(%rsp)
|
||||||
self.code.extend_from_slice(&[0x48, 0x89, 0x84, 0x24]);
|
self.code.extend_from_slice(&[0x48, 0x89, 0x84, 0x24]);
|
||||||
self.code.extend_from_slice(value_to_bytes(&(i * 8u32)));
|
self.code
|
||||||
|
.extend_from_slice(value_to_bytes(&((i as u32) * 8u32)));
|
||||||
|
source_stack_count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -219,6 +320,27 @@ impl TrampolineBufferBuilder {
|
|||||||
idx
|
idx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Inserts this trampoline to the global trampoline buffer.
|
||||||
|
pub fn insert_global(self) -> Option<NonNull<u8>> {
|
||||||
|
TRAMPOLINES.insert(&self.code)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes the trampoline pointed to by `ptr` from the global trampoline buffer. Panics if `ptr`
|
||||||
|
/// does not point to any trampoline.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Calling this function invalidates the trampoline `ptr` points to and recycles its memory. You
|
||||||
|
/// should ensure that `ptr` isn't used after calling `remove_global`.
|
||||||
|
pub unsafe fn remove_global(ptr: NonNull<u8>) {
|
||||||
|
TRAMPOLINES.remove(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the current (non-executable) code in this builder.
|
||||||
|
pub fn code(&self) -> &[u8] {
|
||||||
|
&self.code
|
||||||
|
}
|
||||||
|
|
||||||
/// Consumes the builder and builds the trampoline buffer.
|
/// Consumes the builder and builds the trampoline buffer.
|
||||||
pub fn build(self) -> TrampolineBuffer {
|
pub fn build(self) -> TrampolineBuffer {
|
||||||
get_context(); // ensure lazy initialization is completed
|
get_context(); // ensure lazy initialization is completed
|
||||||
@ -281,8 +403,13 @@ mod tests {
|
|||||||
}
|
}
|
||||||
let mut builder = TrampolineBufferBuilder::new();
|
let mut builder = TrampolineBufferBuilder::new();
|
||||||
let ctx = TestContext { value: 100 };
|
let ctx = TestContext { value: 100 };
|
||||||
let idx =
|
let param_types: Vec<Type> = vec![Type::I32; 8];
|
||||||
builder.add_callinfo_trampoline(do_add, &ctx as *const TestContext as *const _, 8);
|
let idx = builder.add_callinfo_trampoline(
|
||||||
|
do_add,
|
||||||
|
&ctx as *const TestContext as *const _,
|
||||||
|
¶m_types,
|
||||||
|
&[Type::I32],
|
||||||
|
);
|
||||||
let buf = builder.build();
|
let buf = builder.build();
|
||||||
let t = buf.get_trampoline(idx);
|
let t = buf.get_trampoline(idx);
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
@ -292,4 +419,126 @@ mod tests {
|
|||||||
};
|
};
|
||||||
assert_eq!(ret, 136);
|
assert_eq!(ret, 136);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_trampolines_with_floating_point() {
|
||||||
|
unsafe extern "C" fn inner(n: *const CallContext, args: *const u64) -> u64 {
|
||||||
|
// `n` is not really a pointer. It is the length of the argument list, casted into the pointer type.
|
||||||
|
let n = n as usize;
|
||||||
|
let mut result: u64 = 0;
|
||||||
|
for i in 0..n {
|
||||||
|
result += *args.offset(i as _);
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
let buffer = TrampBuffer::new(4096);
|
||||||
|
let mut builder = TrampolineBufferBuilder::new();
|
||||||
|
builder.add_callinfo_trampoline(
|
||||||
|
inner,
|
||||||
|
8 as _,
|
||||||
|
&[
|
||||||
|
Type::I32,
|
||||||
|
Type::I32,
|
||||||
|
Type::I32,
|
||||||
|
Type::F32,
|
||||||
|
Type::I32,
|
||||||
|
Type::I32,
|
||||||
|
Type::I32,
|
||||||
|
Type::I32,
|
||||||
|
],
|
||||||
|
&[Type::I32],
|
||||||
|
);
|
||||||
|
let ptr = buffer.insert(builder.code()).unwrap();
|
||||||
|
let ret = unsafe {
|
||||||
|
let f = std::mem::transmute::<
|
||||||
|
_,
|
||||||
|
extern "C" fn(i32, i32, i32, f32, i32, i32, i32, i32) -> i32,
|
||||||
|
>(ptr);
|
||||||
|
f(1, 2, 3, f32::from_bits(4), 5, 6, 7, 8)
|
||||||
|
};
|
||||||
|
assert_eq!(ret, 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_many_global_trampolines() {
|
||||||
|
unsafe extern "C" fn inner(n: *const CallContext, args: *const u64) -> u64 {
|
||||||
|
// `n` is not really a pointer. It is the length of the argument list, casted into the pointer type.
|
||||||
|
let n = n as usize;
|
||||||
|
let mut result: u64 = 0;
|
||||||
|
for i in 0..n {
|
||||||
|
result += *args.offset(i as _);
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the smallest possible buffer size (page size) to check memory releasing logic.
|
||||||
|
let buffer = TrampBuffer::new(4096);
|
||||||
|
|
||||||
|
// Validate the previous trampoline instead of the current one to ensure that no overwrite happened.
|
||||||
|
let mut prev: Option<(NonNull<u8>, u64)> = None;
|
||||||
|
|
||||||
|
for i in 0..5000usize {
|
||||||
|
let mut builder = TrampolineBufferBuilder::new();
|
||||||
|
let n = i % 8;
|
||||||
|
let param_types: Vec<_> = (0..n).map(|_| Type::I64).collect();
|
||||||
|
builder.add_callinfo_trampoline(inner, n as _, ¶m_types, &[Type::I64]);
|
||||||
|
let ptr = buffer
|
||||||
|
.insert(builder.code())
|
||||||
|
.expect("cannot insert new code into global buffer");
|
||||||
|
|
||||||
|
if let Some((ptr, expected)) = prev.take() {
|
||||||
|
use std::mem::transmute;
|
||||||
|
|
||||||
|
// Test different argument counts.
|
||||||
|
unsafe {
|
||||||
|
match expected {
|
||||||
|
0 => {
|
||||||
|
let f = transmute::<_, extern "C" fn() -> u64>(ptr);
|
||||||
|
assert_eq!(f(), 0);
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
let f = transmute::<_, extern "C" fn(u64) -> u64>(ptr);
|
||||||
|
assert_eq!(f(1), 1);
|
||||||
|
}
|
||||||
|
3 => {
|
||||||
|
let f = transmute::<_, extern "C" fn(u64, u64) -> u64>(ptr);
|
||||||
|
assert_eq!(f(1, 2), 3);
|
||||||
|
}
|
||||||
|
6 => {
|
||||||
|
let f = transmute::<_, extern "C" fn(u64, u64, u64) -> u64>(ptr);
|
||||||
|
assert_eq!(f(1, 2, 3), 6);
|
||||||
|
}
|
||||||
|
10 => {
|
||||||
|
let f = transmute::<_, extern "C" fn(u64, u64, u64, u64) -> u64>(ptr);
|
||||||
|
assert_eq!(f(1, 2, 3, 4), 10);
|
||||||
|
}
|
||||||
|
15 => {
|
||||||
|
let f =
|
||||||
|
transmute::<_, extern "C" fn(u64, u64, u64, u64, u64) -> u64>(ptr);
|
||||||
|
assert_eq!(f(1, 2, 3, 4, 5), 15);
|
||||||
|
}
|
||||||
|
21 => {
|
||||||
|
let f = transmute::<
|
||||||
|
_,
|
||||||
|
extern "C" fn(u64, u64, u64, u64, u64, u64) -> u64,
|
||||||
|
>(ptr);
|
||||||
|
assert_eq!(f(1, 2, 3, 4, 5, 6), 21);
|
||||||
|
}
|
||||||
|
28 => {
|
||||||
|
let f = transmute::<
|
||||||
|
_,
|
||||||
|
extern "C" fn(u64, u64, u64, u64, u64, u64, u64) -> u64,
|
||||||
|
>(ptr);
|
||||||
|
assert_eq!(f(1, 2, 3, 4, 5, 6, 7), 28);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
buffer.remove(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let expected = (0..=n as u64).sum();
|
||||||
|
prev = Some((ptr, expected))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,18 +190,68 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents a type-erased function provided by either the host or the WebAssembly program.
|
||||||
|
pub struct DynamicFunc<'a> {
|
||||||
|
_inner: Box<dyn Kind>,
|
||||||
|
|
||||||
|
/// The function pointer.
|
||||||
|
func: NonNull<vm::Func>,
|
||||||
|
|
||||||
|
/// The function environment.
|
||||||
|
func_env: Option<NonNull<vm::FuncEnv>>,
|
||||||
|
|
||||||
|
/// The famous `vm::Ctx`.
|
||||||
|
vmctx: *mut vm::Ctx,
|
||||||
|
|
||||||
|
/// The runtime signature of this function.
|
||||||
|
///
|
||||||
|
/// When converted from a `Func`, this is determined by the static `Args` and `Rets` type parameters.
|
||||||
|
/// otherwise the signature is dynamically assigned during `DynamicFunc` creation, usually when creating
|
||||||
|
/// a polymorphic host function.
|
||||||
|
signature: Arc<FuncSig>,
|
||||||
|
|
||||||
|
_phantom: PhantomData<&'a ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<'a> Send for DynamicFunc<'a> {}
|
||||||
|
|
||||||
/// Represents a function that can be used by WebAssembly.
|
/// Represents a function that can be used by WebAssembly.
|
||||||
pub struct Func<'a, Args = (), Rets = (), Inner: Kind = Wasm> {
|
pub struct Func<'a, Args = (), Rets = (), Inner: Kind = Wasm> {
|
||||||
inner: Inner,
|
inner: Inner,
|
||||||
|
|
||||||
|
/// The function pointer.
|
||||||
func: NonNull<vm::Func>,
|
func: NonNull<vm::Func>,
|
||||||
|
|
||||||
|
/// The function environment.
|
||||||
func_env: Option<NonNull<vm::FuncEnv>>,
|
func_env: Option<NonNull<vm::FuncEnv>>,
|
||||||
|
|
||||||
|
/// The famous `vm::Ctx`.
|
||||||
vmctx: *mut vm::Ctx,
|
vmctx: *mut vm::Ctx,
|
||||||
|
|
||||||
_phantom: PhantomData<(&'a (), Args, Rets)>,
|
_phantom: PhantomData<(&'a (), Args, Rets)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<'a, Args, Rets> Send for Func<'a, Args, Rets, Wasm> {}
|
unsafe impl<'a, Args, Rets> Send for Func<'a, Args, Rets, Wasm> {}
|
||||||
unsafe impl<'a, Args, Rets> Send for Func<'a, Args, Rets, Host> {}
|
unsafe impl<'a, Args, Rets> Send for Func<'a, Args, Rets, Host> {}
|
||||||
|
|
||||||
|
impl<'a, Args, Rets, Inner> From<Func<'a, Args, Rets, Inner>> for DynamicFunc<'a>
|
||||||
|
where
|
||||||
|
Args: WasmTypeList,
|
||||||
|
Rets: WasmTypeList,
|
||||||
|
Inner: Kind + 'static,
|
||||||
|
{
|
||||||
|
fn from(that: Func<'a, Args, Rets, Inner>) -> DynamicFunc<'a> {
|
||||||
|
DynamicFunc {
|
||||||
|
_inner: Box::new(that.inner),
|
||||||
|
func: that.func,
|
||||||
|
func_env: that.func_env,
|
||||||
|
vmctx: that.vmctx,
|
||||||
|
signature: Arc::new(FuncSig::new(Args::types(), Rets::types())),
|
||||||
|
_phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, Args, Rets> Func<'a, Args, Rets, Wasm>
|
impl<'a, Args, Rets> Func<'a, Args, Rets, Wasm>
|
||||||
where
|
where
|
||||||
Args: WasmTypeList,
|
Args: WasmTypeList,
|
||||||
@ -229,7 +279,7 @@ where
|
|||||||
Rets: WasmTypeList,
|
Rets: WasmTypeList,
|
||||||
{
|
{
|
||||||
/// Creates a new `Func`.
|
/// Creates a new `Func`.
|
||||||
pub fn new<F, Kind>(func: F) -> Func<'a, Args, Rets, Host>
|
pub fn new<F, Kind>(func: F) -> Self
|
||||||
where
|
where
|
||||||
Kind: HostFunctionKind,
|
Kind: HostFunctionKind,
|
||||||
F: HostFunction<Kind, Args, Rets>,
|
F: HostFunction<Kind, Args, Rets>,
|
||||||
@ -246,6 +296,149 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> DynamicFunc<'a> {
|
||||||
|
/// Creates a dynamic function that is polymorphic over its argument and return types.
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
#[cfg(all(unix, target_arch = "x86_64"))]
|
||||||
|
pub fn new<F>(signature: Arc<FuncSig>, func: F) -> Self
|
||||||
|
where
|
||||||
|
F: Fn(&mut vm::Ctx, &[crate::types::Value]) -> Vec<crate::types::Value> + 'static,
|
||||||
|
{
|
||||||
|
use crate::trampoline_x64::{CallContext, TrampolineBufferBuilder};
|
||||||
|
use crate::types::Value;
|
||||||
|
|
||||||
|
struct PolymorphicContext {
|
||||||
|
arg_types: Vec<Type>,
|
||||||
|
func: Box<dyn Fn(&mut vm::Ctx, &[Value]) -> Vec<Value>>,
|
||||||
|
}
|
||||||
|
unsafe fn do_enter_host_polymorphic(
|
||||||
|
ctx: *const CallContext,
|
||||||
|
args: *const u64,
|
||||||
|
) -> Vec<Value> {
|
||||||
|
let ctx = &*(ctx as *const PolymorphicContext);
|
||||||
|
let vmctx = &mut *(*args.offset(0) as *mut vm::Ctx);
|
||||||
|
let args: Vec<Value> = ctx
|
||||||
|
.arg_types
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, t)| {
|
||||||
|
let i = i + 1; // skip vmctx
|
||||||
|
match *t {
|
||||||
|
Type::I32 => Value::I32(*args.offset(i as _) as i32),
|
||||||
|
Type::I64 => Value::I64(*args.offset(i as _) as i64),
|
||||||
|
Type::F32 => Value::F32(f32::from_bits(*args.offset(i as _) as u32)),
|
||||||
|
Type::F64 => Value::F64(f64::from_bits(*args.offset(i as _) as u64)),
|
||||||
|
Type::V128 => {
|
||||||
|
todo!("enter_host_polymorphic: 128-bit types are not supported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
(ctx.func)(vmctx, &args)
|
||||||
|
}
|
||||||
|
unsafe extern "C" fn enter_host_polymorphic_i(
|
||||||
|
ctx: *const CallContext,
|
||||||
|
args: *const u64,
|
||||||
|
) -> u64 {
|
||||||
|
let rets = do_enter_host_polymorphic(ctx, args);
|
||||||
|
if rets.len() == 0 {
|
||||||
|
0
|
||||||
|
} else if rets.len() == 1 {
|
||||||
|
match rets[0] {
|
||||||
|
Value::I32(x) => x as u64,
|
||||||
|
Value::I64(x) => x as u64,
|
||||||
|
_ => panic!("enter_host_polymorphic_i: invalid return type"),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"multiple return values from polymorphic host functions is not yet supported"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe extern "C" fn enter_host_polymorphic_f(
|
||||||
|
ctx: *const CallContext,
|
||||||
|
args: *const u64,
|
||||||
|
) -> f64 {
|
||||||
|
let rets = do_enter_host_polymorphic(ctx, args);
|
||||||
|
if rets.len() == 0 {
|
||||||
|
0.0
|
||||||
|
} else if rets.len() == 1 {
|
||||||
|
match rets[0] {
|
||||||
|
Value::F32(x) => f64::from_bits(x.to_bits() as u64),
|
||||||
|
Value::F64(x) => x,
|
||||||
|
_ => panic!("enter_host_polymorphic_f: invalid return type"),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"multiple return values from polymorphic host functions is not yet supported"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg!(not(feature = "dynamicfunc-fat-closures")) && mem::size_of::<F>() != 0 {
|
||||||
|
unimplemented!("DynamicFunc with captured environment is disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut builder = TrampolineBufferBuilder::new();
|
||||||
|
let ctx: Box<PolymorphicContext> = Box::new(PolymorphicContext {
|
||||||
|
arg_types: signature.params().to_vec(),
|
||||||
|
func: Box::new(func),
|
||||||
|
});
|
||||||
|
let ctx = Box::into_raw(ctx);
|
||||||
|
|
||||||
|
let mut native_param_types = vec![Type::I64]; // vm::Ctx is the first parameter.
|
||||||
|
native_param_types.extend_from_slice(signature.params());
|
||||||
|
|
||||||
|
match signature.returns() {
|
||||||
|
[x] if *x == Type::F32 || *x == Type::F64 => {
|
||||||
|
builder.add_callinfo_trampoline(
|
||||||
|
unsafe { std::mem::transmute(enter_host_polymorphic_f as usize) },
|
||||||
|
ctx as *const _,
|
||||||
|
&native_param_types,
|
||||||
|
signature.returns(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
builder.add_callinfo_trampoline(
|
||||||
|
enter_host_polymorphic_i,
|
||||||
|
ctx as *const _,
|
||||||
|
&native_param_types,
|
||||||
|
signature.returns(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ptr = builder
|
||||||
|
.insert_global()
|
||||||
|
.expect("cannot bump-allocate global trampoline memory");
|
||||||
|
|
||||||
|
struct AutoRelease {
|
||||||
|
ptr: NonNull<u8>,
|
||||||
|
ctx: *mut PolymorphicContext,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for AutoRelease {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
TrampolineBufferBuilder::remove_global(self.ptr);
|
||||||
|
Box::from_raw(self.ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Kind for AutoRelease {}
|
||||||
|
|
||||||
|
DynamicFunc {
|
||||||
|
_inner: Box::new(AutoRelease { ptr, ctx }),
|
||||||
|
func: ptr.cast::<vm::Func>(),
|
||||||
|
func_env: None,
|
||||||
|
vmctx: ptr::null_mut(),
|
||||||
|
signature,
|
||||||
|
_phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, Args, Rets, Inner> Func<'a, Args, Rets, Inner>
|
impl<'a, Args, Rets, Inner> Func<'a, Args, Rets, Inner>
|
||||||
where
|
where
|
||||||
Args: WasmTypeList,
|
Args: WasmTypeList,
|
||||||
@ -674,6 +867,22 @@ impl_traits!([C] S24, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T
|
|||||||
impl_traits!([C] S25, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y);
|
impl_traits!([C] S25, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y);
|
||||||
impl_traits!([C] S26, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z);
|
impl_traits!([C] S26, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z);
|
||||||
|
|
||||||
|
impl<'a> IsExport for DynamicFunc<'a> {
|
||||||
|
fn to_export(&self) -> Export {
|
||||||
|
let func = unsafe { FuncPointer::new(self.func.as_ptr()) };
|
||||||
|
let ctx = match self.func_env {
|
||||||
|
func_env @ Some(_) => Context::ExternalWithEnv(self.vmctx, func_env),
|
||||||
|
None => Context::Internal,
|
||||||
|
};
|
||||||
|
|
||||||
|
Export::Function {
|
||||||
|
func,
|
||||||
|
ctx,
|
||||||
|
signature: self.signature.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, Args, Rets, Inner> IsExport for Func<'a, Args, Rets, Inner>
|
impl<'a, Args, Rets, Inner> IsExport for Func<'a, Args, Rets, Inner>
|
||||||
where
|
where
|
||||||
Args: WasmTypeList,
|
Args: WasmTypeList,
|
||||||
@ -686,12 +895,11 @@ where
|
|||||||
func_env @ Some(_) => Context::ExternalWithEnv(self.vmctx, func_env),
|
func_env @ Some(_) => Context::ExternalWithEnv(self.vmctx, func_env),
|
||||||
None => Context::Internal,
|
None => Context::Internal,
|
||||||
};
|
};
|
||||||
let signature = Arc::new(FuncSig::new(Args::types(), Rets::types()));
|
|
||||||
|
|
||||||
Export::Function {
|
Export::Function {
|
||||||
func,
|
func,
|
||||||
ctx,
|
ctx,
|
||||||
signature,
|
signature: Arc::new(FuncSig::new(Args::types(), Rets::types())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -798,4 +1006,18 @@ mod tests {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_many_new_dynamics() {
|
||||||
|
use crate::types::{FuncSig, Type};
|
||||||
|
|
||||||
|
// Check that generating a lot (1M) of polymorphic functions doesn't use up the executable buffer.
|
||||||
|
for _ in 0..1000000 {
|
||||||
|
let arglist = vec![Type::I32; 100];
|
||||||
|
DynamicFunc::new(
|
||||||
|
Arc::new(FuncSig::new(arglist, vec![Type::I32])),
|
||||||
|
|_, _| unreachable!(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -216,7 +216,16 @@ wasm_extern_type!(f64 => f64);
|
|||||||
// fn swap(&self, other: Self::Primitive) -> Self::Primitive;
|
// fn swap(&self, other: Self::Primitive) -> Self::Primitive;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
/// Trait for a Value type.
|
/// Trait for a Value type. A Value type is a type that is always valid and may
|
||||||
|
/// be safely copied.
|
||||||
|
///
|
||||||
|
/// That is, for all possible bit patterns a valid Value type can be constructed
|
||||||
|
/// from those bits.
|
||||||
|
///
|
||||||
|
/// Concretely a `u32` is a Value type because every combination of 32 bits is
|
||||||
|
/// a valid `u32`. However a `bool` is _not_ a Value type because any bit patterns
|
||||||
|
/// other than `0` and `1` are invalid in Rust and may cause undefined behavior if
|
||||||
|
/// a `bool` is constructed from those bytes.
|
||||||
pub unsafe trait ValueType: Copy
|
pub unsafe trait ValueType: Copy
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
|
@ -545,13 +545,13 @@ impl Ctx {
|
|||||||
/// `typed_func` module within the `wrap` functions, to wrap imported
|
/// `typed_func` module within the `wrap` functions, to wrap imported
|
||||||
/// functions.
|
/// functions.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct Func(pub(self) *mut c_void);
|
pub struct Func(*mut c_void);
|
||||||
|
|
||||||
/// Represents a function environment pointer, like a captured
|
/// Represents a function environment pointer, like a captured
|
||||||
/// environment of a closure. It is mostly used in the `typed_func`
|
/// environment of a closure. It is mostly used in the `typed_func`
|
||||||
/// module within the `wrap` functions, to wrap imported functions.
|
/// module within the `wrap` functions, to wrap imported functions.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct FuncEnv(pub(self) *mut c_void);
|
pub struct FuncEnv(*mut c_void);
|
||||||
|
|
||||||
/// Represents a function context. It is used by imported functions
|
/// Represents a function context. It is used by imported functions
|
||||||
/// only.
|
/// only.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wasmer-runtime"
|
name = "wasmer-runtime"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
description = "Wasmer runtime library"
|
description = "Wasmer runtime library"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||||
@ -11,17 +11,17 @@ edition = "2018"
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.14.1", optional = true }
|
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.2", optional = true }
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
memmap = "0.7"
|
memmap = "0.7"
|
||||||
|
|
||||||
[dependencies.wasmer-runtime-core]
|
[dependencies.wasmer-runtime-core]
|
||||||
path = "../runtime-core"
|
path = "../runtime-core"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
|
|
||||||
[dependencies.wasmer-clif-backend]
|
[dependencies.wasmer-clif-backend]
|
||||||
path = "../clif-backend"
|
path = "../clif-backend"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
# Dependencies for caching.
|
# Dependencies for caching.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wasmer-singlepass-backend"
|
name = "wasmer-singlepass-backend"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
repository = "https://github.com/wasmerio/wasmer"
|
repository = "https://github.com/wasmerio/wasmer"
|
||||||
description = "Wasmer runtime single pass compiler backend"
|
description = "Wasmer runtime single pass compiler backend"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
@ -11,7 +11,7 @@ edition = "2018"
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.14.1" }
|
wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" }
|
||||||
dynasm = "0.5"
|
dynasm = "0.5"
|
||||||
dynasmrt = "0.5"
|
dynasmrt = "0.5"
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
|
@ -32,8 +32,9 @@ use wasmer_runtime_core::{
|
|||||||
memory::MemoryType,
|
memory::MemoryType,
|
||||||
module::{ModuleInfo, ModuleInner},
|
module::{ModuleInfo, ModuleInner},
|
||||||
state::{
|
state::{
|
||||||
x64::new_machine_state, x64::X64Register, FunctionStateMap, MachineState, MachineValue,
|
x64::new_machine_state, x64::X64Register, x64_decl::ArgumentRegisterAllocator,
|
||||||
ModuleStateMap, OffsetInfo, SuspendOffset, WasmAbstractValue,
|
FunctionStateMap, MachineState, MachineValue, ModuleStateMap, OffsetInfo, SuspendOffset,
|
||||||
|
WasmAbstractValue,
|
||||||
},
|
},
|
||||||
structures::{Map, TypedIndex},
|
structures::{Map, TypedIndex},
|
||||||
typed_func::{Trampoline, Wasm},
|
typed_func::{Trampoline, Wasm},
|
||||||
@ -204,6 +205,7 @@ pub struct X64FunctionCode {
|
|||||||
|
|
||||||
signatures: Arc<Map<SigIndex, FuncSig>>,
|
signatures: Arc<Map<SigIndex, FuncSig>>,
|
||||||
function_signatures: Arc<Map<FuncIndex, SigIndex>>,
|
function_signatures: Arc<Map<FuncIndex, SigIndex>>,
|
||||||
|
signature: FuncSig,
|
||||||
fsm: FunctionStateMap,
|
fsm: FunctionStateMap,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
|
|
||||||
@ -712,11 +714,22 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
|
|||||||
machine.track_state = self.config.as_ref().unwrap().track_state;
|
machine.track_state = self.config.as_ref().unwrap().track_state;
|
||||||
|
|
||||||
assembler.emit_label(begin_label);
|
assembler.emit_label(begin_label);
|
||||||
|
|
||||||
|
let signatures = self.signatures.as_ref().unwrap();
|
||||||
|
let function_signatures = self.function_signatures.as_ref().unwrap();
|
||||||
|
let sig_index = function_signatures
|
||||||
|
.get(FuncIndex::new(
|
||||||
|
self.functions.len() + self.func_import_count,
|
||||||
|
))
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
let sig = signatures.get(sig_index).unwrap().clone();
|
||||||
let code = X64FunctionCode {
|
let code = X64FunctionCode {
|
||||||
local_function_id: self.functions.len(),
|
local_function_id: self.functions.len(),
|
||||||
|
|
||||||
signatures: self.signatures.as_ref().unwrap().clone(),
|
signatures: signatures.clone(),
|
||||||
function_signatures: self.function_signatures.as_ref().unwrap().clone(),
|
function_signatures: function_signatures.clone(),
|
||||||
|
signature: sig,
|
||||||
fsm: FunctionStateMap::new(new_machine_state(), self.functions.len(), 32, vec![]), // only a placeholder; this is initialized later in `begin_body`
|
fsm: FunctionStateMap::new(new_machine_state(), self.functions.len(), 32, vec![]), // only a placeholder; this is initialized later in `begin_body`
|
||||||
offset: begin_offset.0,
|
offset: begin_offset.0,
|
||||||
|
|
||||||
@ -869,7 +882,7 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn feed_import_function(&mut self) -> Result<(), CodegenError> {
|
fn feed_import_function(&mut self, sigindex: SigIndex) -> Result<(), CodegenError> {
|
||||||
let labels = self.function_labels.as_mut().unwrap();
|
let labels = self.function_labels.as_mut().unwrap();
|
||||||
let id = labels.len();
|
let id = labels.len();
|
||||||
|
|
||||||
@ -880,6 +893,92 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
|
|||||||
a.emit_label(label);
|
a.emit_label(label);
|
||||||
labels.insert(id, (label, Some(offset)));
|
labels.insert(id, (label, Some(offset)));
|
||||||
|
|
||||||
|
// Singlepass internally treats all arguments as integers, but the standard System V calling convention requires
|
||||||
|
// floating point arguments to be passed in XMM registers.
|
||||||
|
//
|
||||||
|
// FIXME: This is only a workaround. We should fix singlepass to use the standard CC.
|
||||||
|
let sig = self
|
||||||
|
.signatures
|
||||||
|
.as_ref()
|
||||||
|
.expect("signatures itself")
|
||||||
|
.get(sigindex)
|
||||||
|
.expect("signatures");
|
||||||
|
// Translation is expensive, so only do it if needed.
|
||||||
|
if sig
|
||||||
|
.params()
|
||||||
|
.iter()
|
||||||
|
.find(|&&x| x == Type::F32 || x == Type::F64)
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
let mut param_locations: Vec<Location> = vec![];
|
||||||
|
|
||||||
|
// Allocate stack space for arguments.
|
||||||
|
let stack_offset: i32 = if sig.params().len() > 5 {
|
||||||
|
5 * 8
|
||||||
|
} else {
|
||||||
|
(sig.params().len() as i32) * 8
|
||||||
|
};
|
||||||
|
if stack_offset > 0 {
|
||||||
|
a.emit_sub(
|
||||||
|
Size::S64,
|
||||||
|
Location::Imm32(stack_offset as u32),
|
||||||
|
Location::GPR(GPR::RSP),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store all arguments to the stack to prevent overwrite.
|
||||||
|
for i in 0..sig.params().len() {
|
||||||
|
let loc = match i {
|
||||||
|
0..=4 => {
|
||||||
|
static PARAM_REGS: &'static [GPR] =
|
||||||
|
&[GPR::RSI, GPR::RDX, GPR::RCX, GPR::R8, GPR::R9];
|
||||||
|
let loc = Location::Memory(GPR::RSP, (i * 8) as i32);
|
||||||
|
a.emit_mov(Size::S64, Location::GPR(PARAM_REGS[i]), loc);
|
||||||
|
loc
|
||||||
|
}
|
||||||
|
_ => Location::Memory(GPR::RSP, stack_offset + 8 + ((i - 5) * 8) as i32),
|
||||||
|
};
|
||||||
|
param_locations.push(loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy arguments.
|
||||||
|
let mut argalloc = ArgumentRegisterAllocator::default();
|
||||||
|
argalloc.next(Type::I32).unwrap(); // skip vm::Ctx
|
||||||
|
let mut caller_stack_offset: i32 = 0;
|
||||||
|
for (i, ty) in sig.params().iter().enumerate() {
|
||||||
|
let prev_loc = param_locations[i];
|
||||||
|
let target = match argalloc.next(*ty) {
|
||||||
|
Some(X64Register::GPR(gpr)) => Location::GPR(gpr),
|
||||||
|
Some(X64Register::XMM(xmm)) => Location::XMM(xmm),
|
||||||
|
None => {
|
||||||
|
// No register can be allocated. Put this argument on the stack.
|
||||||
|
//
|
||||||
|
// Since here we never use fewer registers than by the original call, on the caller's frame
|
||||||
|
// we always have enough space to store the rearranged arguments, and the copy "backward" between different
|
||||||
|
// slots in the caller argument region will always work.
|
||||||
|
a.emit_mov(Size::S64, prev_loc, Location::GPR(GPR::RAX));
|
||||||
|
a.emit_mov(
|
||||||
|
Size::S64,
|
||||||
|
Location::GPR(GPR::RAX),
|
||||||
|
Location::Memory(GPR::RSP, stack_offset + 8 + caller_stack_offset),
|
||||||
|
);
|
||||||
|
caller_stack_offset += 8;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
a.emit_mov(Size::S64, prev_loc, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore stack pointer.
|
||||||
|
if stack_offset > 0 {
|
||||||
|
a.emit_add(
|
||||||
|
Size::S64,
|
||||||
|
Location::Imm32(stack_offset as u32),
|
||||||
|
Location::GPR(GPR::RSP),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Emits a tail call trampoline that loads the address of the target import function
|
// Emits a tail call trampoline that loads the address of the target import function
|
||||||
// from Ctx and jumps to it.
|
// from Ctx and jumps to it.
|
||||||
|
|
||||||
@ -6260,9 +6359,16 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
|||||||
false,
|
false,
|
||||||
)[0];
|
)[0];
|
||||||
self.value_stack.push(ret);
|
self.value_stack.push(ret);
|
||||||
|
match return_types[0] {
|
||||||
|
WpType::F32 | WpType::F64 => {
|
||||||
|
a.emit_mov(Size::S64, Location::XMM(XMM::XMM0), ret);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret);
|
a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Operator::CallIndirect { index, table_index } => {
|
Operator::CallIndirect { index, table_index } => {
|
||||||
if table_index != 0 {
|
if table_index != 0 {
|
||||||
return Err(CodegenError {
|
return Err(CodegenError {
|
||||||
@ -6399,9 +6505,16 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
|||||||
false,
|
false,
|
||||||
)[0];
|
)[0];
|
||||||
self.value_stack.push(ret);
|
self.value_stack.push(ret);
|
||||||
|
match return_types[0] {
|
||||||
|
WpType::F32 | WpType::F64 => {
|
||||||
|
a.emit_mov(Size::S64, Location::XMM(XMM::XMM0), ret);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret);
|
a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Operator::If { ty } => {
|
Operator::If { ty } => {
|
||||||
let label_end = a.get_label();
|
let label_end = a.get_label();
|
||||||
let label_else = a.get_label();
|
let label_else = a.get_label();
|
||||||
@ -7614,6 +7727,18 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
|||||||
self.machine.finalize_locals(a, &self.locals);
|
self.machine.finalize_locals(a, &self.locals);
|
||||||
a.emit_mov(Size::S64, Location::GPR(GPR::RBP), Location::GPR(GPR::RSP));
|
a.emit_mov(Size::S64, Location::GPR(GPR::RBP), Location::GPR(GPR::RSP));
|
||||||
a.emit_pop(Size::S64, Location::GPR(GPR::RBP));
|
a.emit_pop(Size::S64, Location::GPR(GPR::RBP));
|
||||||
|
|
||||||
|
// Make a copy of the return value in XMM0, as required by the SysV CC.
|
||||||
|
match self.signature.returns() {
|
||||||
|
[x] if *x == Type::F32 || *x == Type::F64 => {
|
||||||
|
a.emit_mov(
|
||||||
|
Size::S64,
|
||||||
|
Location::GPR(GPR::RAX),
|
||||||
|
Location::XMM(XMM::XMM0),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
a.emit_ret();
|
a.emit_ret();
|
||||||
} else {
|
} else {
|
||||||
let released = &self.value_stack[frame.value_stack_depth..];
|
let released = &self.value_stack[frame.value_stack_depth..];
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wasmer-spectests"
|
name = "wasmer-spectests"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
description = "Wasmer spectests library"
|
description = "Wasmer spectests library"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||||
@ -9,10 +9,10 @@ edition = "2018"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
glob = "0.3"
|
glob = "0.3"
|
||||||
wasmer-runtime = { path = "../runtime", version = "0.14.1", default-features = false}
|
wasmer-runtime = { path = "../runtime", version = "0.16.2", default-features = false}
|
||||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.14.1", optional = true}
|
wasmer-clif-backend = { path = "../clif-backend", version = "0.16.2", optional = true}
|
||||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.14.1", features = ["test"], optional = true }
|
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.2", features = ["test"], optional = true }
|
||||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.14.1", optional = true }
|
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.2", optional = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
wabt = "0.9.1"
|
wabt = "0.9.1"
|
||||||
|
@ -256,6 +256,16 @@ mod tests {
|
|||||||
Memory, Table,
|
Memory, Table,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn format_panic(e: &dyn std::any::Any) -> String {
|
||||||
|
if let Some(s) = e.downcast_ref::<&str>() {
|
||||||
|
format!("{}", s)
|
||||||
|
} else if let Some(s) = e.downcast_ref::<String>() {
|
||||||
|
format!("{}", s)
|
||||||
|
} else {
|
||||||
|
"(unknown)".into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_and_run(
|
fn parse_and_run(
|
||||||
path: &PathBuf,
|
path: &PathBuf,
|
||||||
file_excludes: &HashSet<String>,
|
file_excludes: &HashSet<String>,
|
||||||
@ -342,7 +352,7 @@ mod tests {
|
|||||||
file: filename.to_string(),
|
file: filename.to_string(),
|
||||||
line: line,
|
line: line,
|
||||||
kind: format!("{}", "Module"),
|
kind: format!("{}", "Module"),
|
||||||
message: format!("caught panic {:?}", e),
|
message: format!("caught panic {}", format_panic(&e)),
|
||||||
},
|
},
|
||||||
&test_key,
|
&test_key,
|
||||||
excludes,
|
excludes,
|
||||||
@ -798,7 +808,7 @@ mod tests {
|
|||||||
file: filename.to_string(),
|
file: filename.to_string(),
|
||||||
line: line,
|
line: line,
|
||||||
kind: format!("{}", "AssertInvalid"),
|
kind: format!("{}", "AssertInvalid"),
|
||||||
message: format!("caught panic {:?}", p),
|
message: format!("caught panic {}", format_panic(&p)),
|
||||||
},
|
},
|
||||||
&test_key,
|
&test_key,
|
||||||
excludes,
|
excludes,
|
||||||
@ -851,7 +861,7 @@ mod tests {
|
|||||||
file: filename.to_string(),
|
file: filename.to_string(),
|
||||||
line: line,
|
line: line,
|
||||||
kind: format!("{}", "AssertMalformed"),
|
kind: format!("{}", "AssertMalformed"),
|
||||||
message: format!("caught panic {:?}", p),
|
message: format!("caught panic {}", format_panic(&p)),
|
||||||
},
|
},
|
||||||
&test_key,
|
&test_key,
|
||||||
excludes,
|
excludes,
|
||||||
@ -975,7 +985,7 @@ mod tests {
|
|||||||
file: filename.to_string(),
|
file: filename.to_string(),
|
||||||
line: line,
|
line: line,
|
||||||
kind: format!("{}", "AssertUnlinkable"),
|
kind: format!("{}", "AssertUnlinkable"),
|
||||||
message: format!("caught panic {:?}", e),
|
message: format!("caught panic {}", format_panic(&e)),
|
||||||
},
|
},
|
||||||
&test_key,
|
&test_key,
|
||||||
excludes,
|
excludes,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wasmer-wasi-experimental-io-devices"
|
name = "wasmer-wasi-experimental-io-devices"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
repository = "https://github.com/wasmerio/wasmer"
|
repository = "https://github.com/wasmerio/wasmer"
|
||||||
@ -14,8 +14,8 @@ maintenance = { status = "experimental" }
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
minifb = "0.13"
|
minifb = "0.13"
|
||||||
wasmer-wasi = { version = "0.14.1", path = "../wasi" }
|
wasmer-wasi = { version = "0.16.2", path = "../wasi" }
|
||||||
wasmer-runtime-core = { version = "0.14.1", path = "../runtime-core" }
|
wasmer-runtime-core = { version = "0.16.2", path = "../runtime-core" }
|
||||||
ref_thread_local = "0.0"
|
ref_thread_local = "0.0"
|
||||||
serde = "1"
|
serde = "1"
|
||||||
typetag = "0.1"
|
typetag = "0.1"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wasmer-wasi-tests"
|
name = "wasmer-wasi-tests"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
description = "Tests for our WASI implementation"
|
description = "Tests for our WASI implementation"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||||
@ -10,18 +10,18 @@ build = "build/mod.rs"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# We set default features to false to be able to use the singlepass backend properly
|
# We set default features to false to be able to use the singlepass backend properly
|
||||||
wasmer-runtime = { path = "../runtime", version = "0.14.1", default-features = false }
|
wasmer-runtime = { path = "../runtime", version = "0.16.2", default-features = false }
|
||||||
wasmer-wasi = { path = "../wasi", version = "0.14.1" }
|
wasmer-wasi = { path = "../wasi", version = "0.16.2" }
|
||||||
# hack to get tests to work
|
# hack to get tests to work
|
||||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.14.1", optional = true}
|
wasmer-clif-backend = { path = "../clif-backend", version = "0.16.2", optional = true}
|
||||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.14.1", optional = true }
|
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.2", optional = true }
|
||||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.14.1", features = ["test"], optional = true }
|
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.2", features = ["test"], optional = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
glob = "0.3"
|
glob = "0.3"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
wasmer-dev-utils = { path = "../dev-utils", version = "0.14.1"}
|
wasmer-dev-utils = { path = "../dev-utils", version = "0.16.2"}
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
clif = ["wasmer-clif-backend", "wasmer-runtime/default-backend-cranelift"]
|
clif = ["wasmer-clif-backend", "wasmer-runtime/default-backend-cranelift"]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wasmer-wasi"
|
name = "wasmer-wasi"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
description = "Wasmer runtime WASI implementation library"
|
description = "Wasmer runtime WASI implementation library"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||||
@ -19,7 +19,7 @@ getrandom = "0.1"
|
|||||||
time = "0.1"
|
time = "0.1"
|
||||||
typetag = "0.1"
|
typetag = "0.1"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.14.1" }
|
wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" }
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winapi = "0.3"
|
winapi = "0.3"
|
||||||
|
@ -1077,6 +1077,15 @@ impl WasiFs {
|
|||||||
fs_rights_inheriting: 0,
|
fs_rights_inheriting: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
VIRTUAL_ROOT_FD => {
|
||||||
|
return Ok(__wasi_fdstat_t {
|
||||||
|
fs_filetype: __WASI_FILETYPE_DIRECTORY,
|
||||||
|
fs_flags: 0,
|
||||||
|
// TODO: fix this
|
||||||
|
fs_rights_base: ALL_RIGHTS,
|
||||||
|
fs_rights_inheriting: ALL_RIGHTS,
|
||||||
|
});
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
let fd = self.get_fd(fd)?;
|
let fd = self.get_fd(fd)?;
|
||||||
|
@ -370,8 +370,7 @@ pub fn fd_allocate(
|
|||||||
/// - `__WASI_EBADF`
|
/// - `__WASI_EBADF`
|
||||||
/// If `fd` is invalid or not open
|
/// If `fd` is invalid or not open
|
||||||
pub fn fd_close(ctx: &mut Ctx, fd: __wasi_fd_t) -> __wasi_errno_t {
|
pub fn fd_close(ctx: &mut Ctx, fd: __wasi_fd_t) -> __wasi_errno_t {
|
||||||
debug!("wasi::fd_close");
|
debug!("wasi::fd_close: fd={}", fd);
|
||||||
debug!("=> fd={}", fd);
|
|
||||||
let (memory, state) = get_memory_and_wasi_state(ctx, 0);
|
let (memory, state) = get_memory_and_wasi_state(ctx, 0);
|
||||||
|
|
||||||
let fd_entry = wasi_try!(state.fs.get_fd(fd));
|
let fd_entry = wasi_try!(state.fs.get_fd(fd));
|
||||||
@ -649,7 +648,7 @@ pub fn fd_pread(
|
|||||||
offset: __wasi_filesize_t,
|
offset: __wasi_filesize_t,
|
||||||
nread: WasmPtr<u32>,
|
nread: WasmPtr<u32>,
|
||||||
) -> __wasi_errno_t {
|
) -> __wasi_errno_t {
|
||||||
debug!("wasi::fd_pread");
|
debug!("wasi::fd_pread: fd={}, offset={}", fd, offset);
|
||||||
let (memory, state) = get_memory_and_wasi_state(ctx, 0);
|
let (memory, state) = get_memory_and_wasi_state(ctx, 0);
|
||||||
|
|
||||||
let iov_cells = wasi_try!(iovs.deref(memory, 0, iovs_len));
|
let iov_cells = wasi_try!(iovs.deref(memory, 0, iovs_len));
|
||||||
@ -674,6 +673,10 @@ pub fn fd_pread(
|
|||||||
if !(has_rights(fd_entry.rights, __WASI_RIGHT_FD_READ)
|
if !(has_rights(fd_entry.rights, __WASI_RIGHT_FD_READ)
|
||||||
&& has_rights(fd_entry.rights, __WASI_RIGHT_FD_SEEK))
|
&& has_rights(fd_entry.rights, __WASI_RIGHT_FD_SEEK))
|
||||||
{
|
{
|
||||||
|
debug!(
|
||||||
|
"Invalid rights on {:X}: expected READ and SEEK",
|
||||||
|
fd_entry.rights
|
||||||
|
);
|
||||||
return __WASI_EACCES;
|
return __WASI_EACCES;
|
||||||
}
|
}
|
||||||
match &mut state.fs.inodes[inode].kind {
|
match &mut state.fs.inodes[inode].kind {
|
||||||
@ -699,6 +702,7 @@ pub fn fd_pread(
|
|||||||
};
|
};
|
||||||
|
|
||||||
nread_cell.set(bytes_read);
|
nread_cell.set(bytes_read);
|
||||||
|
debug!("Success: {} bytes read", bytes_read);
|
||||||
__WASI_ESUCCESS
|
__WASI_ESUCCESS
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -971,24 +975,38 @@ pub fn fd_readdir(
|
|||||||
let mut cur_cookie = cookie;
|
let mut cur_cookie = cookie;
|
||||||
let mut buf_idx = 0;
|
let mut buf_idx = 0;
|
||||||
|
|
||||||
let entries = match &state.fs.inodes[working_dir.inode].kind {
|
let entries: Vec<(String, u8, u64)> = match &state.fs.inodes[working_dir.inode].kind {
|
||||||
Kind::Dir { path, .. } => {
|
Kind::Dir { path, entries, .. } => {
|
||||||
// TODO: refactor this code
|
// TODO: refactor this code
|
||||||
// we need to support multiple calls,
|
// we need to support multiple calls,
|
||||||
// simple and obviously correct implementation for now:
|
// simple and obviously correct implementation for now:
|
||||||
// maintain consistent order via lexacographic sorting
|
// maintain consistent order via lexacographic sorting
|
||||||
let mut entries = wasi_try!(wasi_try!(std::fs::read_dir(path).map_err(|_| __WASI_EIO))
|
let fs_info = wasi_try!(wasi_try!(std::fs::read_dir(path).map_err(|_| __WASI_EIO))
|
||||||
.collect::<Result<Vec<std::fs::DirEntry>, _>>()
|
.collect::<Result<Vec<_>, _>>()
|
||||||
.map_err(|_| __WASI_EIO));
|
.map_err(|_| __WASI_EIO));
|
||||||
entries.sort_by(|a, b| a.file_name().cmp(&b.file_name()));
|
let mut entry_vec = wasi_try!(fs_info
|
||||||
wasi_try!(entries
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|entry| Ok((
|
.map(|entry| Ok((
|
||||||
entry.file_name().to_string_lossy().to_string(),
|
entry.file_name().to_string_lossy().to_string(),
|
||||||
host_file_type_to_wasi_file_type(entry.file_type().map_err(|_| __WASI_EIO)?),
|
host_file_type_to_wasi_file_type(entry.file_type().map_err(|_| __WASI_EIO)?),
|
||||||
0, // TODO: inode
|
0, // TODO: inode
|
||||||
)))
|
)))
|
||||||
.collect::<Result<Vec<(String, u8, u64)>, __wasi_errno_t>>())
|
.collect::<Result<Vec<(String, u8, u64)>, _>>());
|
||||||
|
entry_vec.extend(
|
||||||
|
entries
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, inode)| state.fs.inodes[**inode].is_preopened)
|
||||||
|
.map(|(name, inode)| {
|
||||||
|
let entry = &state.fs.inodes[*inode];
|
||||||
|
(
|
||||||
|
format!("{}", entry.name),
|
||||||
|
entry.stat.st_filetype,
|
||||||
|
entry.stat.st_ino,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
entry_vec.sort_by(|a, b| a.0.cmp(&b.0));
|
||||||
|
entry_vec
|
||||||
}
|
}
|
||||||
Kind::Root { entries } => {
|
Kind::Root { entries } => {
|
||||||
let sorted_entries = {
|
let sorted_entries = {
|
||||||
@ -1435,10 +1453,14 @@ pub fn path_filestat_get(
|
|||||||
path_string,
|
path_string,
|
||||||
flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0,
|
flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0,
|
||||||
));
|
));
|
||||||
let stat = wasi_try!(state
|
let stat = if state.fs.inodes[file_inode].is_preopened {
|
||||||
|
state.fs.inodes[file_inode].stat.clone()
|
||||||
|
} else {
|
||||||
|
wasi_try!(state
|
||||||
.fs
|
.fs
|
||||||
.get_stat_for_kind(&state.fs.inodes[file_inode].kind)
|
.get_stat_for_kind(&state.fs.inodes[file_inode].kind)
|
||||||
.ok_or(__WASI_EIO));
|
.ok_or(__WASI_EIO))
|
||||||
|
};
|
||||||
|
|
||||||
let buf_cell = wasi_try!(buf.deref(memory));
|
let buf_cell = wasi_try!(buf.deref(memory));
|
||||||
buf_cell.set(stat);
|
buf_cell.set(stat);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wasmer-win-exception-handler"
|
name = "wasmer-win-exception-handler"
|
||||||
version = "0.14.1"
|
version = "0.16.2"
|
||||||
description = "Wasmer runtime exception handling for Windows"
|
description = "Wasmer runtime exception handling for Windows"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||||
@ -8,7 +8,7 @@ repository = "https://github.com/wasmerio/wasmer"
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.14.1" }
|
wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" }
|
||||||
winapi = { version = "0.3.8", features = ["winbase", "errhandlingapi", "minwindef", "minwinbase", "winnt"] }
|
winapi = { version = "0.3.8", features = ["winbase", "errhandlingapi", "minwindef", "minwinbase", "winnt"] }
|
||||||
libc = "0.2.60"
|
libc = "0.2.60"
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
PREVIOUS_VERSION='0.14.0'
|
PREVIOUS_VERSION='0.16.1'
|
||||||
NEXT_VERSION='0.14.1'
|
NEXT_VERSION='0.16.2'
|
||||||
|
|
||||||
# quick hack
|
# quick hack
|
||||||
fd Cargo.toml --exec sed -i '' "s/version = \"$PREVIOUS_VERSION\"/version = \"$NEXT_VERSION\"/"
|
fd Cargo.toml --exec sed -i '' "s/version = \"$PREVIOUS_VERSION\"/version = \"$NEXT_VERSION\"/"
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 22 KiB |
Binary file not shown.
Before Width: | Height: | Size: 958 KiB After Width: | Height: | Size: 1.2 MiB |
Binary file not shown.
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 12 KiB |
@ -1,6 +1,6 @@
|
|||||||
[Setup]
|
[Setup]
|
||||||
AppName=Wasmer
|
AppName=Wasmer
|
||||||
AppVersion=0.14.1
|
AppVersion=0.16.2
|
||||||
DefaultDirName={pf}\Wasmer
|
DefaultDirName={pf}\Wasmer
|
||||||
DefaultGroupName=Wasmer
|
DefaultGroupName=Wasmer
|
||||||
Compression=lzma2
|
Compression=lzma2
|
||||||
@ -23,6 +23,7 @@ Root: HKCU; Subkey: "Environment"; ValueType:string; ValueName: "WASMER_CACHE_DI
|
|||||||
[Files]
|
[Files]
|
||||||
Source: "..\..\target\release\wasmer.exe"; DestDir: "{app}\bin"
|
Source: "..\..\target\release\wasmer.exe"; DestDir: "{app}\bin"
|
||||||
Source: "..\..\wapm-cli\target\release\wapm.exe"; DestDir: "{app}\bin"
|
Source: "..\..\wapm-cli\target\release\wapm.exe"; DestDir: "{app}\bin"
|
||||||
|
Source: "wax.cmd"; DestDir: "{app}\bin"
|
||||||
|
|
||||||
[Dirs]
|
[Dirs]
|
||||||
Name: "{%USERPROFILE}\.wasmer"
|
Name: "{%USERPROFILE}\.wasmer"
|
||||||
|
2
src/installer/wax.cmd
Normal file
2
src/installer/wax.cmd
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
@echo off
|
||||||
|
wapm.exe execute %*
|
@ -46,7 +46,11 @@ pub fn set_up_logging() -> Result<(), String> {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
base.chain(std::io::stdout())
|
base
|
||||||
|
.filter(|metadata| {
|
||||||
|
metadata.target().starts_with("wasmer")
|
||||||
|
})
|
||||||
|
.chain(std::io::stdout())
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch.apply().map_err(|e| format!("{}", e))?;
|
dispatch.apply().map_err(|e| format!("{}", e))?;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user