Merge master.

This commit is contained in:
losfair
2019-11-11 01:42:43 +08:00
209 changed files with 11116 additions and 4774 deletions

View File

@ -25,6 +25,19 @@ steps:
Write-Host "##vso[task.setvariable variable=RUSTC_WRAPPER;]$pwd/sccache-0.2.10-x86_64-pc-windows-msvc/sccache.exe" Write-Host "##vso[task.setvariable variable=RUSTC_WRAPPER;]$pwd/sccache-0.2.10-x86_64-pc-windows-msvc/sccache.exe"
displayName: Install sccache - Windows displayName: Install sccache - Windows
condition: eq( variables['Agent.OS'], 'Windows_NT' ) condition: eq( variables['Agent.OS'], 'Windows_NT' )
- bash: |
set -ex
env
SCCACHE_ERROR_LOG=`pwd`/sccache.log RUST_LOG=debug $RUSTC_WRAPPER --start-server
$RUSTC_WRAPPER -s
cat sccache.log
displayName: "start sccache"
condition: not(eq( variables['Agent.OS'], 'Windows_NT' ))
env:
SCCACHE_AZURE_CONNECTION_STRING: $(SCCACHE_AZURE_CONNECTION_STRING)
SCCACHE_AZURE_BLOB_CONTAINER: $(SCCACHE_AZURE_BLOB_CONTAINER)
# Only use Azure pipelines cache in Windows
- bash: | - bash: |
set -ex set -ex
env env
@ -33,12 +46,12 @@ steps:
$RUSTC_WRAPPER -s $RUSTC_WRAPPER -s
cat sccache.log cat sccache.log
displayName: "start sccache" displayName: "start sccache"
condition: eq( variables['Agent.OS'], 'Windows_NT' )
env: env:
SCCACHE_AZURE_CONNECTION_STRING: $(SCCACHE_AZURE_CONNECTION_STRING)
SCCACHE_AZURE_BLOB_CONTAINER: $(SCCACHE_AZURE_BLOB_CONTAINER)
SCCACHE_DIR: $(Pipeline.Workspace)/.sccache SCCACHE_DIR: $(Pipeline.Workspace)/.sccache
- task: CacheBeta@0 - task: CacheBeta@0
inputs: inputs:
key: sccache | $(Agent.OS) | Cargo.lock key: sccache | $(Agent.OS) | Cargo.lock
path: $(Pipeline.Workspace)/.sccache path: $(Pipeline.Workspace)/.sccache
displayName: Cache Cargo Target displayName: Cache Cargo Target
condition: eq( variables['Agent.OS'], 'Windows_NT' )

1
.gitattributes vendored
View File

@ -1,2 +1,3 @@
lib/emscripten/emtests/* linguist-vendored lib/emscripten/emtests/* linguist-vendored
lib/spectests/spectests/* linguist-vendored lib/spectests/spectests/* linguist-vendored
CHANGELOG.md merge=union

1
.github/CODEOWNERS vendored
View File

@ -8,7 +8,6 @@ lib/llvm-backend @nlewycky @losfair
# Runtime # Runtime
lib/runtime-core @Hywan @bjfish lib/runtime-core @Hywan @bjfish
lib/runtime-abi @MarkMcCaskey
lib/runtime @MarkMcCaskey @Hywan @bjfish lib/runtime @MarkMcCaskey @Hywan @bjfish
lib/runtime-c-api @bjfish @Hywan lib/runtime-c-api @bjfish @Hywan
lib/win-exception-handler @bjfish @losfair lib/win-exception-handler @bjfish @losfair

View File

@ -7,37 +7,44 @@ assignees: ''
--- ---
Thanks for the bug report! <!-- Thanks for the bug report! -->
### Describe the bug ### Describe the bug
<!--
A clear and concise description of what the bug is. A clear and concise description of what the bug is.
Copy and paste the result of executing the following in your shell, so we can know the version of wasmer, Rust (if available) and architecture of your environment. Copy and paste the result of executing the following in your shell, so we can know the version of wasmer, Rust (if available) and architecture of your environment.
-->
```bash ```sh
echo "`wasmer -V` | `rustc -V` | `uname -m`" echo "`wasmer -V` | `rustc -V` | `uname -m`"
``` ```
### Steps to reproduce
### Steps to reproduce
<!--
Include steps that will help us recreate the issue.
For example,
1. Go to '…' 1. Go to '…'
2. Compile with '…' 2. Compile with '…'
3. Run '…' 3. Run '…'
4. See error 4. See error
If applicable, add a link to a test case (as a zip file or link to a repository we can clone). If applicable, add a link to a test case (as a zip file or link to a repository we can clone).
-->
### Expected behavior ### Expected behavior
<!-- A clear and concise description of what you expected to happen. -->
A clear and concise description of what you expected to happen.
### Actual behavior ### Actual behavior
<!--
A clear and concise description of what actually happened. A clear and concise description of what actually happened.
If applicable, add screenshots to help explain your problem. If applicable, add screenshots to help explain your problem.
-->
### Additional context ### Additional context
<!-- Add any other context about the problem here. -->
Add any other context about the problem here.

15
.github/pull_request_template.md vendored Normal file
View File

@ -0,0 +1,15 @@
<!--
Prior to submitting a PR, review the CONTRIBUTING.md document for recommendations on how to test:
https://github.com/wasmerio/wasmer/blob/master/CONTRIBUTING.md#pull-requests
-->
# Description
<!--
Provide details regarding the change including motivation,
links to related issues, and the context of the PR.
-->
# Review
- [ ] Add a short description of the the change to the CHANGELOG.md file

View File

@ -1,13 +1,75 @@
# Changelog # Changelog
All PRs to the Wasmer repository must add to this file.
Blocks of changes will separated by version increments.
## **[Unreleased]** ## **[Unreleased]**
- [#936](https://github.com/wasmerio/wasmer/pull/939) Fix bug causing attempts to append to files with WASI to delete the contents of the file
- [#940](https://github.com/wasmerio/wasmer/pull/940) Update supported Rust version to 1.38+
- [#921](https://github.com/wasmerio/wasmer/pull/921) In LLVM backend, annotate all memory accesses with TBAA metadata.
- [#883](https://github.com/wasmerio/wasmer/pull/883) Allow floating point operations to have arbitrary inputs, even including SNaNs.
- [#856](https://github.com/wasmerio/wasmer/pull/856) Expose methods in the runtime C API to get a WASI import object
## 0.9.0 - 2019-10-23
Special thanks to @alocquet for their contributions!
- [#898](https://github.com/wasmerio/wasmer/pull/898) State tracking is now disabled by default in the LLVM backend. It can be enabled with `--track-state`.
- [#861](https://github.com/wasmerio/wasmer/pull/861) Add descriptions to `unimplemented!` macro in various places
- [#897](https://github.com/wasmerio/wasmer/pull/897) Removes special casing of stdin, stdout, and stderr in WASI. Closing these files now works. Removes `stdin`, `stdout`, and `stderr` from `WasiFS`, replaced by the methods `stdout`, `stdout_mut`, and so on.
- [#863](https://github.com/wasmerio/wasmer/pull/863) Fix min and max for cases involving NaN and negative zero when using the LLVM backend.
## 0.8.0 - 2019-10-02
Special thanks to @jdanford for their contributions!
- [#850](https://github.com/wasmerio/wasmer/pull/850) New `WasiStateBuilder` API. small, add misc. breaking changes to existing API (for example, changing the preopen dirs arg on `wasi::generate_import_object` from `Vec<String>` to `Vec<Pathbuf>`)
- [#852](https://github.com/wasmerio/wasmer/pull/852) Make minor grammar/capitalization fixes to README.md
- [#841](https://github.com/wasmerio/wasmer/pull/841) Slightly improve rustdoc documentation and small updates to outdated info in readme files
- [#836](https://github.com/wasmerio/wasmer/pull/836) Update Cranelift fork version to `0.44.0`
- [#839](https://github.com/wasmerio/wasmer/pull/839) Change supported version to stable Rust 1.37+
- [#834](https://github.com/wasmerio/wasmer/pull/834) Fix panic when unwraping `wasmer` arguments
- [#835](https://github.com/wasmerio/wasmer/pull/835) Add parallel execution example (independent instances created from the same `ImportObject` and `Module` run with rayon)
- [#834](https://github.com/wasmerio/wasmer/pull/834) Fix panic when parsing numerical arguments for no-ABI targets run with the wasmer binary
- [#833](https://github.com/wasmerio/wasmer/pull/833) Add doc example of using ImportObject's new `maybe_with_namespace` method
- [#832](https://github.com/wasmerio/wasmer/pull/832) Delete unused runtime ABI
- [#809](https://github.com/wasmerio/wasmer/pull/809) Fix bugs leading to panics in `LocalBacking`.
- [#831](https://github.com/wasmerio/wasmer/pull/831) Add support for atomic operations, excluding wait and notify, to singlepass.
- [#822](https://github.com/wasmerio/wasmer/pull/822) Update Cranelift fork version to `0.43.1`
- [#829](https://github.com/wasmerio/wasmer/pull/829) Fix deps on `make bench-*` commands; benchmarks don't compile other backends now
- [#807](https://github.com/wasmerio/wasmer/pull/807) Implement Send for `Instance`, breaking change on `ImportObject`, remove method `get_namespace` replaced with `with_namespace` and `maybe_with_namespace`
- [#817](https://github.com/wasmerio/wasmer/pull/817) Add document for tracking features across backends and language integrations, [docs/feature_matrix.md]
- [#823](https://github.com/wasmerio/wasmer/issues/823) Improved Emscripten / WASI integration
- [#821](https://github.com/wasmerio/wasmer/issues/821) Remove patch version on most deps Cargo manifests. This gives Wasmer library users more control over which versions of the deps they use.
- [#820](https://github.com/wasmerio/wasmer/issues/820) Remove null-pointer checks in `WasmPtr` from runtime-core, re-add them in Emscripten
- [#803](https://github.com/wasmerio/wasmer/issues/803) Add method to `Ctx` to invoke functions by their `TableIndex`
- [#790](https://github.com/wasmerio/wasmer/pull/790) Fix flaky test failure with LLVM, switch to large code model.
- [#788](https://github.com/wasmerio/wasmer/pull/788) Use union merge on the changelog file.
- [#785](https://github.com/wasmerio/wasmer/pull/785) Include Apache license file for spectests.
- [#786](https://github.com/wasmerio/wasmer/pull/786) In the LLVM backend, lower atomic wasm operations to atomic machine instructions.
- [#784](https://github.com/wasmerio/wasmer/pull/784) Fix help string for wasmer run.
## 0.7.0 - 2019-09-12
Special thanks to @YaronWittenstein @penberg for their contributions. Special thanks to @YaronWittenstein @penberg for their contributions.
- [#776](https://github.com/wasmerio/wasmer/issues/776) Allow WASI preopened fds to be closed
- [#774](https://github.com/wasmerio/wasmer/issues/774) Add more methods to the `WasiFile` trait
- [#772](https://github.com/wasmerio/wasmer/issues/772) [#770](https://github.com/wasmerio/wasmer/issues/770) Handle more internal failures by passing back errors
- [#756](https://github.com/wasmerio/wasmer/issues/756) Allow NULL parameter and 0 arity in `wasmer_export_func_call` C API
- [#747](https://github.com/wasmerio/wasmer/issues/747) Return error instead of panicking on traps when using the Wasmer binary
- [#741](https://github.com/wasmerio/wasmer/issues/741) Add validate Wasm fuzz target
- [#733](https://github.com/wasmerio/wasmer/issues/733) Remove dependency on compiler backends for `middleware-common`
- [#732](https://github.com/wasmerio/wasmer/issues/732) [#731](https://github.com/wasmerio/wasmer/issues/731) WASI bug fixes and improvements
- [#726](https://github.com/wasmerio/wasmer/issues/726) Add serialization and deserialization for Wasi State
- [#716](https://github.com/wasmerio/wasmer/issues/716) Improve portability of install script
- [#714](https://github.com/wasmerio/wasmer/issues/714) Add Code of Conduct
- [#708](https://github.com/wasmerio/wasmer/issues/708) Remove unconditional dependency on Cranelift in the C API
- [#703](https://github.com/wasmerio/wasmer/issues/703) Fix compilation on AArch64 Linux
- [#702](https://github.com/wasmerio/wasmer/issues/702) Add SharedMemory to Wasmer. Add `--enable-threads` flag, add partial implementation of atomics to LLVM backend.
- [#698](https://github.com/wasmerio/wasmer/issues/698) [#690](https://github.com/wasmerio/wasmer/issues/690) [#687](https://github.com/wasmerio/wasmer/issues/690) Fix panics in Emscripten
- [#689](https://github.com/wasmerio/wasmer/issues/689) Replace `wasmer_runtime_code::memory::Atomic` with `std::sync::atomic` atomics, changing its interface
- [#680](https://github.com/wasmerio/wasmer/issues/680) [#673](https://github.com/wasmerio/wasmer/issues/673) [#669](https://github.com/wasmerio/wasmer/issues/669) [#660](https://github.com/wasmerio/wasmer/issues/660) [#659](https://github.com/wasmerio/wasmer/issues/659) Misc. runtime and singlepass fixes
- [#677](https://github.com/wasmerio/wasmer/issues/677) [#675](https://github.com/wasmerio/wasmer/issues/675) [#674](https://github.com/wasmerio/wasmer/issues/674) LLVM backend fixes and improvements
- [#671](https://github.com/wasmerio/wasmer/issues/671) Implement fs polling in `wasi::poll_oneoff` for Unix-like platforms
- [#656](https://github.com/wasmerio/wasmer/issues/656) Move CI to Azure Pipelines - [#656](https://github.com/wasmerio/wasmer/issues/656) Move CI to Azure Pipelines
- [#650](https://github.com/wasmerio/wasmer/issues/650) Implement `wasi::path_rename`, improve WASI FS public api, and allow open files to exist even when the underlying file is deleted - [#650](https://github.com/wasmerio/wasmer/issues/650) Implement `wasi::path_rename`, improve WASI FS public api, and allow open files to exist even when the underlying file is deleted
- [#643](https://github.com/wasmerio/wasmer/issues/643) Implement `wasi::path_symlink` and improve WASI FS public api IO error reporting - [#643](https://github.com/wasmerio/wasmer/issues/643) Implement `wasi::path_symlink` and improve WASI FS public api IO error reporting
@ -82,7 +144,7 @@ Special thanks to @YaronWittenstein @penberg for their contributions.
- [#493](https://github.com/wasmerio/wasmer/pull/493) `wasmer_module_instantiate` has better error messages in the runtime C API - [#493](https://github.com/wasmerio/wasmer/pull/493) `wasmer_module_instantiate` has better error messages in the runtime C API
- [#474](https://github.com/wasmerio/wasmer/pull/474) Set the install name of the dylib to `@rpath` - [#474](https://github.com/wasmerio/wasmer/pull/474) Set the install name of the dylib to `@rpath`
- [#490](https://github.com/wasmerio/wasmer/pull/490) Add MiddlewareChain and StreamingCompiler to runtime - [#490](https://github.com/wasmerio/wasmer/pull/490) Add MiddlewareChain and StreamingCompiler to runtime
- [#487](https://github.com/wasmerio/wasmer/pull/487) Fix stack offset check in singlepass backend - [#487](https://github.com/wasmerio/wasmer/pull/487) Fix stack offset check in singlepass backend
- [#450](https://github.com/wasmerio/wasmer/pull/450) Added Metering - [#450](https://github.com/wasmerio/wasmer/pull/450) Added Metering
- [#481](https://github.com/wasmerio/wasmer/pull/481) Added context trampoline into runtime - [#481](https://github.com/wasmerio/wasmer/pull/481) Added context trampoline into runtime
- [#484](https://github.com/wasmerio/wasmer/pull/484) Fix bugs in emscripten socket syscalls - [#484](https://github.com/wasmerio/wasmer/pull/484) Fix bugs in emscripten socket syscalls

View File

@ -3,12 +3,15 @@
Thank you for your interest in contributing to Wasmer. This document outlines some recommendations on how to contribute. Thank you for your interest in contributing to Wasmer. This document outlines some recommendations on how to contribute.
## Issues & Feature Requests ## Issues & Feature Requests
Please use the issue template and provide a failing example if possible to help us recreate the issue. Please use the issue template and provide a failing example if possible to help us recreate the issue.
## Pull Requests ## Pull Requests
For large changes, please try reaching the Wasmer using Github Issues or Spectrum Chat to ensure we can accept the change once it is ready.
For large changes, please try reaching communicating with the Wasmer maintainers via GitHub Issues or Spectrum Chat to ensure we can accept the change once it is ready.
We recommend trying the following commands before sending a pull request to ensure code quality: We recommend trying the following commands before sending a pull request to ensure code quality:
- `cargo fmt --all` Ensures all code is correctly formatted. - `cargo fmt --all` Ensures all code is correctly formatted.
- Run `cargo test` in the crates that you are modifying. - Run `cargo test` in the crates that you are modifying.
- Run `cargo build --all` (nightly) or `cargo build --all --exclude wasmer-singlepass-backend` - Run `cargo build --all` (nightly) or `cargo build --all --exclude wasmer-singlepass-backend`
@ -17,17 +20,14 @@ A comprehensive CI test suite will be run by a Wasmer team member after the PR h
### Common Build Issues ### Common Build Issues
**LLVM Dependency** #### LLVM Dependency
The LLVM backend requires LLVM to be installed to compile. `Didn't find usable system-wide LLVM`
So, you may run into the following error: Building Wasmer with the LLVM backend requires LLVM to be installed
```
Didn't find usable system-wide LLVM.
No suitable version of LLVM was found system-wide or pointed
```
**Singlepass Nightly Only** #### Singlepass Nightly Only
The singlepass crate depends on nightly so you may need to add the `+nightly` cargo flag to compile this crate.
`error[E0554]: #![feature] may not be used on the stable release channel` `error[E0554]: #![feature] may not be used on the stable release channel`
Building Wasmer with the singlepass backend requires the nightly version of Rust

1114
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,13 @@
[package] [package]
name = "wasmer" name = "wasmer"
version = "0.6.0" version = "0.9.0"
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"
publish = true publish = true
description = "High-Performance WebAssembly JIT interpreter" description = "High-Performance WebAssembly JIT interpreter"
license = "MIT" license = "MIT"
default-run = "wasmer"
include = [ include = [
"examples/**/*", "examples/**/*",
"src/**/*", "src/**/*",
@ -19,15 +20,14 @@ include = [
] ]
[dependencies] [dependencies]
byteorder = "1.3.2" byteorder = "1.3"
errno = "0.2.4" errno = "0.2"
structopt = "0.2.18" structopt = "0.3"
wabt = "0.9.1" wabt = "0.9.1"
wasmer-clif-backend = { path = "lib/clif-backend" } wasmer-clif-backend = { path = "lib/clif-backend" }
wasmer-singlepass-backend = { path = "lib/singlepass-backend", optional = true } wasmer-singlepass-backend = { path = "lib/singlepass-backend", optional = true }
wasmer-middleware-common = { path = "lib/middleware-common" } wasmer-middleware-common = { path = "lib/middleware-common" }
wasmer-runtime = { path = "lib/runtime" } wasmer-runtime = { path = "lib/runtime" }
# wasmer-runtime-abi = { path = "lib/runtime-abi", optional = true }
wasmer-runtime-core = { path = "lib/runtime-core" } wasmer-runtime-core = { path = "lib/runtime-core" }
wasmer-emscripten = { path = "lib/emscripten" } wasmer-emscripten = { path = "lib/emscripten" }
wasmer-llvm-backend = { path = "lib/llvm-backend", optional = true } wasmer-llvm-backend = { path = "lib/llvm-backend", optional = true }
@ -43,8 +43,8 @@ members = [
"lib/clif-backend", "lib/clif-backend",
"lib/singlepass-backend", "lib/singlepass-backend",
"lib/runtime", "lib/runtime",
# "lib/runtime-abi",
"lib/runtime-core", "lib/runtime-core",
"lib/runtime-core-tests",
"lib/emscripten", "lib/emscripten",
"lib/spectests", "lib/spectests",
"lib/win-exception-handler", "lib/win-exception-handler",
@ -59,13 +59,14 @@ members = [
"lib/emscripten-tests", "lib/emscripten-tests",
"lib/middleware-common-tests", "lib/middleware-common-tests",
"examples/plugin-for-example", "examples/plugin-for-example",
"examples/hello_world" "examples/parallel",
"examples/parallel-guest",
] ]
[build-dependencies] [build-dependencies]
wabt = "0.9.1" wabt = "0.9.1"
glob = "0.3.0" glob = "0.3"
rustc_version = "0.2.3" rustc_version = "0.2"
[dev-dependencies] [dev-dependencies]
serde = { version = "1", features = ["derive"] } # used by the plugin example serde = { version = "1", features = ["derive"] } # used by the plugin example
@ -101,8 +102,11 @@ backend-singlepass = [
] ]
wasi = ["wasmer-wasi"] wasi = ["wasmer-wasi"]
managed = ["backend-singlepass", "wasmer-runtime-core/managed"] managed = ["backend-singlepass", "wasmer-runtime-core/managed"]
# vfs = ["wasmer-runtime-abi"]
[[example]] [[example]]
name = "plugin" name = "plugin"
crate-type = ["bin"] crate-type = ["bin"]
[[example]]
name = "callback"
crate-type = ["bin"]

105
Makefile
View File

@ -1,11 +1,15 @@
.PHONY: spectests emtests clean build install lint precommit docs .PHONY: spectests emtests clean build install lint precommit docs examples
# Generate files # Generate files
generate-spectests: generate-spectests:
WASMER_RUNTIME_GENERATE_SPECTESTS=1 cargo build -p wasmer-runtime-core --release WASMER_RUNTIME_GENERATE_SPECTESTS=1 cargo build -p wasmer-runtime-core --release \
&& echo "formatting" \
&& cargo fmt
generate-emtests: generate-emtests:
WASM_EMSCRIPTEN_GENERATE_EMTESTS=1 cargo build -p wasmer-emscripten-tests --release WASM_EMSCRIPTEN_GENERATE_EMTESTS=1 cargo build -p wasmer-emscripten-tests --release \
&& echo "formatting" \
&& cargo fmt
generate-wasitests: wasitests-setup generate-wasitests: wasitests-setup
WASM_WASI_GENERATE_WASITESTS=1 cargo build -p wasmer-wasi-tests --release -vv \ WASM_WASI_GENERATE_WASITESTS=1 cargo build -p wasmer-wasi-tests --release -vv \
@ -85,12 +89,15 @@ wasitests: wasitests-unit wasitests-singlepass wasitests-cranelift wasitests-llv
# Backends # Backends
singlepass: spectests-singlepass emtests-singlepass middleware-singlepass wasitests-singlepass singlepass: spectests-singlepass emtests-singlepass middleware-singlepass wasitests-singlepass
cargo test -p wasmer-singlepass-backend --release cargo test -p wasmer-singlepass-backend --release
cargo test -p wasmer-runtime-core-tests --release --no-default-features --features backend-singlepass
cranelift: spectests-cranelift emtests-cranelift middleware-cranelift wasitests-cranelift cranelift: spectests-cranelift emtests-cranelift middleware-cranelift wasitests-cranelift
cargo test -p wasmer-clif-backend --release cargo test -p wasmer-clif-backend --release
cargo test -p wasmer-runtime-core-tests --release
llvm: spectests-llvm emtests-llvm wasitests-llvm llvm: spectests-llvm emtests-llvm wasitests-llvm
cargo test -p wasmer-llvm-backend --release cargo test -p wasmer-llvm-backend --release
cargo test -p wasmer-runtime-core-tests --release --no-default-features --features backend-llvm
# All tests # All tests
@ -101,8 +108,23 @@ capi:
test-capi: capi test-capi: capi
cargo test -p wasmer-runtime-c-api --release cargo test -p wasmer-runtime-c-api --release
capi-test: test-capi
test-rest: test-rest:
cargo test --release --all --exclude wasmer-runtime-c-api --exclude wasmer-emscripten --exclude wasmer-spectests --exclude wasmer-wasi --exclude wasmer-middleware-common --exclude wasmer-middleware-common-tests --exclude wasmer-singlepass-backend --exclude wasmer-clif-backend --exclude wasmer-llvm-backend --exclude wasmer-wasi-tests --exclude wasmer-emscripten-tests cargo test --release \
--all \
--exclude wasmer-runtime-c-api \
--exclude wasmer-emscripten \
--exclude wasmer-spectests \
--exclude wasmer-wasi \
--exclude wasmer-middleware-common \
--exclude wasmer-middleware-common-tests \
--exclude wasmer-singlepass-backend \
--exclude wasmer-clif-backend \
--exclude wasmer-llvm-backend \
--exclude wasmer-wasi-tests \
--exclude wasmer-emscripten-tests \
--exclude wasmer-runtime-core-tests
circleci-clean: circleci-clean:
@if [ ! -z "${CIRCLE_JOB}" ]; then rm -f /home/circleci/project/target/debug/deps/libcranelift_wasm* && rm -f /Users/distiller/project/target/debug/deps/libcranelift_wasm*; fi; @if [ ! -z "${CIRCLE_JOB}" ]; then rm -f /home/circleci/project/target/debug/deps/libcranelift_wasm* && rm -f /Users/distiller/project/target/debug/deps/libcranelift_wasm*; fi;
@ -111,12 +133,16 @@ test: spectests emtests middleware wasitests circleci-clean test-rest
# Integration tests # Integration tests
integration-tests: release-clif integration-tests: release-clif examples
echo "Running Integration Tests" echo "Running Integration Tests"
./integration_tests/lua/test.sh ./integration_tests/lua/test.sh
./integration_tests/nginx/test.sh ./integration_tests/nginx/test.sh
./integration_tests/cowsay/test.sh ./integration_tests/cowsay/test.sh
examples:
cargo run --example plugin
cargo run --example callback
# Utils # Utils
lint: lint:
@ -132,16 +158,65 @@ install:
# Checks # Checks
check-bench-singlepass: check-bench-singlepass:
cargo bench --all --no-run --no-default-features --features "backend-singlepass" cargo check --benches --all --no-default-features --features "backend-singlepass" \
--exclude wasmer-clif-backend --exclude wasmer-llvm-backend --exclude wasmer-kernel-loader
check-bench-clif: check-bench-clif:
cargo bench --all --no-run --no-default-features --features "backend-cranelift" cargo check --benches --all --no-default-features --features "backend-cranelift" \
--exclude wasmer-singlepass-backend --exclude wasmer-llvm-backend --exclude wasmer-kernel-loader \
--exclude wasmer-middleware-common-tests
check-bench-llvm: check-bench-llvm:
cargo bench --all --no-run --no-default-features --features "backend-llvm" cargo check --benches --all --no-default-features --features "backend-llvm" \
--exclude wasmer-singlepass-backend --exclude wasmer-clif-backend --exclude wasmer-kernel-loader
check-bench: check-bench-singlepass check-bench-llvm check-bench: check-bench-singlepass check-bench-llvm
# TODO: We wanted `--workspace --exclude wasmer-runtime`, but can't due
# to https://github.com/rust-lang/cargo/issues/6745 .
NOT_RUNTIME_CRATES = -p wasmer-clif-backend -p wasmer-singlepass-backend -p wasmer-middleware-common -p wasmer-runtime-core -p wasmer-emscripten -p wasmer-llvm-backend -p wasmer-wasi -p wasmer-kernel-loader -p wasmer-dev-utils -p wasmer-wasi-tests -p wasmer-middleware-common-tests -p wasmer-emscripten-tests
RUNTIME_CHECK = cargo check --manifest-path lib/runtime/Cargo.toml --no-default-features
check: check-bench check: check-bench
cargo check --release --features backend-singlepass,backend-llvm,loader-kernel,debug cargo check $(NOT_RUNTIME_CRATES)
cargo check --release $(NOT_RUNTIME_CRATES)
cargo check --all-features $(NOT_RUNTIME_CRATES)
cargo check --release --all-features $(NOT_RUNTIME_CRATES)
# wasmer-runtime doesn't work with all backends enabled at once.
#
# We test using manifest-path directly so as to disable the default.
# `--no-default-features` only disables the default features in the
# current package, not the package specified by `-p`. This is
# intentional.
#
# Test default features, test 'debug' feature only in non-release
# builds, test as many combined features as possible with each backend
# as default, and test a minimal set of features with only one backend
# at a time.
cargo check --manifest-path lib/runtime/Cargo.toml
cargo check --release --manifest-path lib/runtime/Cargo.toml
$(RUNTIME_CHECK) \
--features=cranelift,cache,debug,llvm,singlepass,default-backend-singlepass
$(RUNTIME_CHECK) --release \
--features=cranelift,cache,llvm,singlepass,default-backend-singlepass
$(RUNTIME_CHECK) \
--features=cranelift,cache,debug,llvm,singlepass,default-backend-cranelift
$(RUNTIME_CHECK) --release \
--features=cranelift,cache,llvm,singlepass,default-backend-cranelift
$(RUNTIME_CHECK) \
--features=cranelift,cache,debug,llvm,singlepass,default-backend-llvm
$(RUNTIME_CHECK) --release \
--features=cranelift,cache,llvm,singlepass,default-backend-llvm
$(RUNTIME_CHECK) \
--features=singlepass,default-backend-singlepass,debug
$(RUNTIME_CHECK) --release \
--features=singlepass,default-backend-singlepass
$(RUNTIME_CHECK) \
--features=cranelift,default-backend-cranelift,debug
$(RUNTIME_CHECK) --release \
--features=cranelift,default-backend-cranelift
$(RUNTIME_CHECK) \
--features=llvm,default-backend-llvm,debug
$(RUNTIME_CHECK) --release \
--features=llvm,default-backend-llvm
# Release # Release
release: release:
@ -149,7 +224,7 @@ release:
# Only one backend (cranelift) # Only one backend (cranelift)
release-clif: release-clif:
# If you are in OS-X, you will need mingw-w64 for cross compiling to windows # If you are on macOS, you will need mingw-w64 for cross compiling to Windows
# brew install mingw-w64 # brew install mingw-w64
cargo build --release cargo build --release
@ -160,11 +235,15 @@ release-llvm:
cargo build --release --features backend-llvm cargo build --release --features backend-llvm
bench-singlepass: bench-singlepass:
cargo bench --all --no-default-features --features "backend-singlepass" cargo bench --all --no-default-features --features "backend-singlepass" \
--exclude wasmer-clif-backend --exclude wasmer-llvm-backend --exclude wasmer-kernel-loader
bench-clif: bench-clif:
cargo bench --all --no-default-features --features "backend-cranelift" cargo bench --all --no-default-features --features "backend-cranelift" \
--exclude wasmer-singlepass-backend --exclude wasmer-llvm-backend --exclude wasmer-kernel-loader \
--exclude wasmer-middleware-common-tests
bench-llvm: bench-llvm:
cargo bench --all --no-default-features --features "backend-llvm" cargo bench --all --no-default-features --features "backend-llvm" \
--exclude wasmer-singlepass-backend --exclude wasmer-clif-backend --exclude wasmer-kernel-loader
# Build utils # Build utils
build-install: build-install:

View File

@ -21,7 +21,7 @@
## Introduction ## Introduction
[Wasmer](https://wasmer.io/) is a standalone WebAssembly runtime, for running WebAssembly [outside of the Browser](https://webassembly.org/docs/non-web/), supporting [WASI](https://github.com/WebAssembly/WASI) and [Emscripten](https://emscripten.org/). [Wasmer](https://wasmer.io/) is a standalone WebAssembly runtime for running WebAssembly [outside of the browser](https://webassembly.org/docs/non-web/), supporting [WASI](https://github.com/WebAssembly/WASI) and [Emscripten](https://emscripten.org/).
Install the Wasmer CLI with: Install the Wasmer CLI with:
@ -44,9 +44,10 @@ Wasmer runtime can be used as a library embedded in different languages, so you
| ![PHP logo](./docs/assets/languages/php.svg) | [**PHP**](https://github.com/wasmerio/php-ext-wasm) | Wasmer | actively developed | <a href="https://pecl.php.net/package/wasm" target="_blank">![last release](https://img.shields.io/github/v/release/wasmerio/php-ext-wasm?style=flat-square)</a> | ![number of Github stars](https://img.shields.io/github/stars/wasmerio/php-ext-wasm?style=flat-square) | | ![PHP logo](./docs/assets/languages/php.svg) | [**PHP**](https://github.com/wasmerio/php-ext-wasm) | Wasmer | actively developed | <a href="https://pecl.php.net/package/wasm" target="_blank">![last release](https://img.shields.io/github/v/release/wasmerio/php-ext-wasm?style=flat-square)</a> | ![number of Github stars](https://img.shields.io/github/stars/wasmerio/php-ext-wasm?style=flat-square) |
| ![Ruby logo](./docs/assets/languages/ruby.svg) | [**Ruby**](https://github.com/wasmerio/ruby-ext-wasm) | Wasmer | actively developed | <a href="https://rubygems.org/gems/wasmer" target="_blank">![last release](https://img.shields.io/gem/v/wasmer?style=flat-square)</a> | ![number of Github stars](https://img.shields.io/github/stars/wasmerio/ruby-ext-wasm?style=flat-square) | | ![Ruby logo](./docs/assets/languages/ruby.svg) | [**Ruby**](https://github.com/wasmerio/ruby-ext-wasm) | Wasmer | actively developed | <a href="https://rubygems.org/gems/wasmer" target="_blank">![last release](https://img.shields.io/gem/v/wasmer?style=flat-square)</a> | ![number of Github stars](https://img.shields.io/github/stars/wasmerio/ruby-ext-wasm?style=flat-square) |
| ![Postgres logo](./docs/assets/languages/postgres.svg) | [**Postgres**](https://github.com/wasmerio/postgres-ext-wasm) | Wasmer | actively developed | <a href="https://github.com/wasmerio/postgres-ext-wasm" target="_blank">![last release](https://img.shields.io/github/v/release/wasmerio/postgres-ext-wasm?style=flat-square)</a> | ![number of Github stars](https://img.shields.io/github/stars/wasmerio/postgres-ext-wasm?style=flat-square) | | ![Postgres logo](./docs/assets/languages/postgres.svg) | [**Postgres**](https://github.com/wasmerio/postgres-ext-wasm) | Wasmer | actively developed | <a href="https://github.com/wasmerio/postgres-ext-wasm" target="_blank">![last release](https://img.shields.io/github/v/release/wasmerio/postgres-ext-wasm?style=flat-square)</a> | ![number of Github stars](https://img.shields.io/github/stars/wasmerio/postgres-ext-wasm?style=flat-square) |
| ![JS Logo](./docs/assets/languages/js.svg) | [**JavaScript**](https://github.com/wasmerio/wasmer-js) | Wasmer | actively developed | <a href="https://www.npmjs.com/package/@wasmer/wasi" target="_blank">![last release](https://img.shields.io/npm/v/@wasmer/wasi?style=flat-square)</a> | ![number of Github stars](https://img.shields.io/github/stars/wasmerio/wasmer-js?style=flat-square) |
| ![C# logo](./docs/assets/languages/csharp.svg) | [**C#/.Net**](https://github.com/migueldeicaza/WasmerSharp) | [Miguel de Icaza](https://github.com/migueldeicaza) | actively developed | <a href="https://www.nuget.org/packages/WasmerSharp/" target="_blank">![last release](https://img.shields.io/nuget/v/WasmerSharp?style=flat-square)</a> | ![number of Github stars](https://img.shields.io/github/stars/migueldeicaza/WasmerSharp?style=flat-square) | | ![C# logo](./docs/assets/languages/csharp.svg) | [**C#/.Net**](https://github.com/migueldeicaza/WasmerSharp) | [Miguel de Icaza](https://github.com/migueldeicaza) | actively developed | <a href="https://www.nuget.org/packages/WasmerSharp/" target="_blank">![last release](https://img.shields.io/nuget/v/WasmerSharp?style=flat-square)</a> | ![number of Github stars](https://img.shields.io/github/stars/migueldeicaza/WasmerSharp?style=flat-square) |
| ![R logo](./docs/assets/languages/r.svg) | [**R**](https://github.com/dirkschumacher/wasmr) | [Dirk Schumacher](https://github.com/dirkschumacher) | actively developed | | ![number of Github stars](https://img.shields.io/github/stars/dirkschumacher/wasmr?style=flat-square) | | ![R logo](./docs/assets/languages/r.svg) | [**R**](https://github.com/dirkschumacher/wasmr) | [Dirk Schumacher](https://github.com/dirkschumacher) | actively developed | | ![number of Github stars](https://img.shields.io/github/stars/dirkschumacher/wasmr?style=flat-square) |
| ![Swift logo](./docs/assets/languages/swift.svg) | [**Swift**](https://github.com/markmals/swift-ext-wasm) | [Mark Malström](https://github.com/markmals/) | passively maintened | | ![number of Github stars](https://img.shields.io/github/stars/markmals/swift-ext-wasm?style=flat-square) | | ![Swift logo](./docs/assets/languages/swift.svg) | [**Swift**](https://github.com/markmals/swift-ext-wasm) | [Mark Malström](https://github.com/markmals/) | passively maintained | | ![number of Github stars](https://img.shields.io/github/stars/markmals/swift-ext-wasm?style=flat-square) |
| ❓ | [your language is missing?](https://github.com/wasmerio/wasmer/issues/new?assignees=&labels=%F0%9F%8E%89+enhancement&template=---feature-request.md&title=) | | | | | ❓ | [your language is missing?](https://github.com/wasmerio/wasmer/issues/new?assignees=&labels=%F0%9F%8E%89+enhancement&template=---feature-request.md&title=) | | | |
### Usage ### Usage
@ -63,12 +64,12 @@ wasmer run examples/lua.wasm
*You can find more `wasm/wat` examples in the [examples](./examples) directory.* *You can find more `wasm/wat` examples in the [examples](./examples) directory.*
#### With WAPM #### With wapm
Installing Wasmer through `wasmer.io` includes Installing Wasmer through `wasmer.io` includes
[`wapm`](https://github.com/wasmerio/wapm-cli), the [WebAssembly Package Manager](https://wapm.io/). [`wapm`](https://github.com/wasmerio/wapm-cli), the [WebAssembly Package Manager](https://wapm.io/).
Wapm allows you to easily download, run, and distribute WebAssembly binaries. wapm allows you to easily download, run, and distribute WebAssembly binaries.
```sh ```sh
# Install cowsay globally # Install cowsay globally
@ -87,7 +88,7 @@ Wasmer is structured into different directories:
- [`src`](./src): code related to the Wasmer executable itself - [`src`](./src): code related to the Wasmer executable itself
- [`lib`](./lib): modularized libraries that Wasmer uses under the hood - [`lib`](./lib): modularized libraries that Wasmer uses under the hood
- [`examples`](./examples): some useful examples to getting started with Wasmer - [`examples`](./examples): some useful examples for getting started with Wasmer
## Dependencies ## Dependencies
@ -118,7 +119,7 @@ If you have [Homebrew](https://brew.sh/) installed:
brew install cmake brew install cmake
``` ```
Or, in case you have [MacPorts](https://www.macports.org/install.php): Or, if you have [MacPorts](https://www.macports.org/install.php):
```sh ```sh
sudo port install cmake sudo port install cmake
@ -136,6 +137,7 @@ sudo port install cmake
```sh ```sh
sudo apt install cmake pkg-config libssl-dev sudo apt install cmake pkg-config libssl-dev
``` ```
</p> </p>
</details> </details>
@ -148,6 +150,7 @@ sudo apt install cmake pkg-config libssl-dev
```sh ```sh
pkg install cmake pkg install cmake
``` ```
</p> </p>
</details> </details>
@ -157,7 +160,7 @@ pkg install cmake
#### Windows (MSVC) #### Windows (MSVC)
Windows support is _experimental_. WASI is fully supported, but Emscripten support is on the works (this means Windows support is _experimental_. WASI is fully supported, but Emscripten support is in the works (this means
nginx and Lua do not work on Windows - you can track the progress on [this issue](https://github.com/wasmerio/wasmer/issues/176)). nginx and Lua do not work on Windows - you can track the progress on [this issue](https://github.com/wasmerio/wasmer/issues/176)).
1. Install [Visual Studio](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=15) 1. Install [Visual Studio](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=15)
@ -173,24 +176,27 @@ nginx and Lua do not work on Windows - you can track the progress on [this issue
5. Install [CMake](https://cmake.org/download/). Ensure CMake is in your PATH. 5. Install [CMake](https://cmake.org/download/). Ensure CMake is in your PATH.
6. Install [LLVM 8.0](https://prereleases.llvm.org/win-snapshots/LLVM-8.0.0-r351033-win64.exe) 6. Install [LLVM 8.0](https://prereleases.llvm.org/win-snapshots/LLVM-8.0.0-r351033-win64.exe)
</p> </p>
</details> </details>
## Building ## Building
[![Rustc Version 1.36+](https://img.shields.io/badge/rustc-1.36+-red.svg?style=flat-square)](https://blog.rust-lang.org/2019/07/04/Rust-1.36.0.html)
[![Rustc Version 1.38+](https://img.shields.io/badge/rustc-1.37+-red.svg?style=flat-square)](https://blog.rust-lang.org/2019/09/26/Rust-1.38.0.html)
Wasmer is built with [Cargo](https://crates.io/), the Rust package manager. Wasmer is built with [Cargo](https://crates.io/), the Rust package manager.
The Singlepass backend requires nightly, so if you want to use it, The Singlepass backend requires nightly, so if you want to use it,
Set Rust Nightly: Set Rust Nightly:
``` ```
rustup default nightly rustup default nightly
``` ```
Otherwise an up to date (see badge above) verison of stable Rust will work. Otherwise an up to date (see badge above) version of stable Rust will work.
And install Wasmer And install Wasmer
```sh ```sh
# checkout code # checkout code
git clone https://github.com/wasmerio/wasmer.git git clone https://github.com/wasmerio/wasmer.git
@ -236,7 +242,6 @@ Each integration can be tested separately:
* Middleware: `make middleware` * Middleware: `make middleware`
* C API: `make capi` * C API: `make capi`
## Benchmarking ## Benchmarking
Benchmarks can be run with: Benchmarks can be run with:
@ -258,7 +263,7 @@ Below are some of the goals of this project (in order of priority):
- [x] It should be fast _(partially achieved)_ - [x] It should be fast _(partially achieved)_
- [x] Support WASI - released in [0.3.0](https://github.com/wasmerio/wasmer/releases/tag/0.3.0) - [x] Support WASI - released in [0.3.0](https://github.com/wasmerio/wasmer/releases/tag/0.3.0)
- [x] Support Emscripten calls _(in the works)_ - [x] Support Emscripten calls _(in the works)_
- [ ] Support Go js ABI calls - [ ] Support Go JS ABI calls
## Architecture ## Architecture

View File

@ -22,7 +22,7 @@ jobs:
- script: cargo fmt --all -- --check - script: cargo fmt --all -- --check
displayName: Lint displayName: Lint
variables: variables:
rust_toolchain: stable rust_toolchain: '1.38.0'
- job: Test - job: Test
strategy: strategy:
@ -39,7 +39,7 @@ jobs:
CARGO_HTTP_CHECK_REVOKE: false CARGO_HTTP_CHECK_REVOKE: false
windows: windows:
imageName: "vs2017-win2016" imageName: "vs2017-win2016"
rust_toolchain: stable rust_toolchain: '1.38.0'
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')
@ -50,6 +50,20 @@ jobs:
- template: .azure/install-llvm.yml - template: .azure/install-llvm.yml
- template: .azure/install-sccache.yml - template: .azure/install-sccache.yml
- template: .azure/install-cmake.yml - template: .azure/install-cmake.yml
- bash: |
hostname
uname -a
displayName: System info (*nix)
condition: and(succeeded(), not(eq(variables['Agent.OS'], 'Windows_NT')))
- bash: |
cat /proc/cpuinfo
cat /proc/meminfo
displayName: System info - Extended (Linux)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
- bash: |
sysctl -a | grep machdep.cpu
displayName: System info - Extended (Mac)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Darwin'))
- bash: make test - bash: make test
displayName: Tests (*nix) displayName: Tests (*nix)
condition: and(succeeded(), not(eq(variables['Agent.OS'], 'Windows_NT'))) condition: and(succeeded(), not(eq(variables['Agent.OS'], 'Windows_NT')))
@ -86,7 +100,7 @@ jobs:
MACOSX_DEPLOYMENT_TARGET: 10.10 MACOSX_DEPLOYMENT_TARGET: 10.10
windows: windows:
imageName: "vs2017-win2016" imageName: "vs2017-win2016"
rust_toolchain: stable rust_toolchain: '1.38.0'
# RUSTFLAGS: -Ctarget-feature=+crt-static # RUSTFLAGS: -Ctarget-feature=+crt-static
pool: pool:
vmImage: $(imageName) vmImage: $(imageName)
@ -149,7 +163,7 @@ jobs:
MACOSX_DEPLOYMENT_TARGET: 10.10 MACOSX_DEPLOYMENT_TARGET: 10.10
windows: windows:
imageName: "vs2017-win2016" imageName: "vs2017-win2016"
rust_toolchain: stable rust_toolchain: '1.38.0'
# RUSTFLAGS: -Ctarget-feature=+crt-static # RUSTFLAGS: -Ctarget-feature=+crt-static
pool: pool:
vmImage: $(imageName) vmImage: $(imageName)

8
docs/assets/languages/js.svg Executable file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<svg width="20" height="20" viewBox="0 0 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
<g>
<path d="M0,0 L256,0 L256,256 L0,256 L0,0 Z" fill="#F7DF1E"></path>
<path d="M67.311746,213.932292 L86.902654,202.076241 C90.6821079,208.777346 94.1202286,214.447137 102.367086,214.447137 C110.272203,214.447137 115.256076,211.354819 115.256076,199.326883 L115.256076,117.528787 L139.313575,117.528787 L139.313575,199.666997 C139.313575,224.58433 124.707759,235.925943 103.3984,235.925943 C84.1532952,235.925943 72.9819429,225.958603 67.3113397,213.93026" fill="#000000"></path>
<path d="M152.380952,211.354413 L171.969422,200.0128 C177.125994,208.433981 183.827911,214.619835 195.684368,214.619835 C205.652521,214.619835 212.009041,209.635962 212.009041,202.762159 C212.009041,194.513676 205.479416,191.592025 194.481168,186.78207 L188.468419,184.202565 C171.111213,176.81473 159.597308,167.53534 159.597308,147.944838 C159.597308,129.901308 173.344508,116.153295 194.825752,116.153295 C210.119924,116.153295 221.117765,121.48094 229.021663,135.400432 L210.29059,147.428775 C206.166146,140.040127 201.699556,137.119289 194.826159,137.119289 C187.78047,137.119289 183.312254,141.587098 183.312254,147.428775 C183.312254,154.646349 187.78047,157.568406 198.089956,162.036622 L204.103924,164.614095 C224.553448,173.378641 236.067352,182.313448 236.067352,202.418387 C236.067352,224.071924 219.055137,235.927975 196.200432,235.927975 C173.860978,235.927975 159.425829,225.274311 152.381359,211.354413" fill="#000000"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

42
docs/feature_matrix.md Normal file
View File

@ -0,0 +1,42 @@
# Feature Table
## Compiler Backend
| &nbsp; | Singlepass | Cranelift | LLVM |
| - | :-: | :-: | :-: |
| Caching | ⬜ | ✅ | ✅ |
| Emscripten | ✅ | ✅ | ✅ |
| Metering | ✅ | ⬜ | ✅ |
| Multi-value return | ⬜ | ⬜ | ⬜ |
| OSR | 🔄 | ❓ | ❓ |
| SIMD | ⬜ | ⬜ | ✅ |
| WASI | ✅ | ✅ | ✅ |
| WASMER_BACKTRACE | ✅ | ⬜ | ⬜ |
## Operating System
| &nbsp; | GNU Linux | Mac OSX | Windows NT |
| - | :-: | :-: | :-: |
| Cranelift Backend | ✅ | ✅ | ✅ |
| LLVM Backend | ✅ | ✅ | ✅ |
| Singlepass Backend | [#347](https://github.com/wasmerio/wasmer/issues/347) | ✅ | ✅ |
| WASI | ✅ | ✅ | ✅* |
* `poll_fd` is not fully implemented for Windows yet
## Language integration
TODO: define a set of features that are relevant and mark them here
Current ideas:
- Callbacks
- Metering
- Caching
> TODO: expand this table, it's focused on new features that we haven't implemented yet and doesn't list all language integrations
| &nbsp; | Rust | C / C++ | Go | Python | Ruby |
| - | :-: | :-: | :-: | :-: | :-: |
| Terminate in host call | ✅ | ⬜ | ⬜ | ⬜ | ⬜ |
| WASI | ✅ | ✅ | 🔄 | ⬜ | ⬜ |
| WASI FS API | ✅ | ⬜ | ⬜ | ⬜ | ⬜ |

View File

@ -0,0 +1,5 @@
# Call back guest
This is part of the `callback` example. This Wasm module passes host imports and its own functions to the Wasm host to execute.
See `examples/callback.rs` for the host

View File

@ -0,0 +1,24 @@
extern "C" {
fn call_guest_fn(f: u32) -> u32;
fn call_guest_fn2(f: u32) -> u32;
fn host_callback() -> u32;
}
#[no_mangle]
fn test_callback() -> u32 {
42
}
#[no_mangle]
fn test_callback2() -> u32 {
45
}
fn main() {
unsafe { call_guest_fn(test_callback as usize as u32) };
unsafe { call_guest_fn(host_callback as usize as u32) };
unsafe { call_guest_fn(test_callback2 as usize as u32) };
unsafe { call_guest_fn2(test_callback2 as usize as u32) };
unsafe { call_guest_fn2(test_callback as usize as u32) };
unsafe { call_guest_fn2(host_callback as usize as u32) };
}

Binary file not shown.

46
examples/callback.rs Normal file
View File

@ -0,0 +1,46 @@
/// This example demonstrates the use of callbacks: calling functions (Host and Wasm)
/// passed to us from the Wasm via hostcall
use wasmer_runtime::{compile_with, compiler_for_backend, func, imports, Backend, Ctx};
use wasmer_runtime_core::{structures::TypedIndex, types::TableIndex};
static WASM: &'static str = "examples/callback-guest/callback-guest.wasm";
/// This function matches our arbitrarily decided callback signature
/// in this example we'll only call functions that take no arguments and return one value
fn host_callback(_ctx: &mut Ctx) -> u32 {
55
}
fn call_guest_fn(ctx: &mut Ctx, guest_fn: u32) -> u32 {
// We get a TableIndex from our raw value passed in
let guest_fn_typed = TableIndex::new(guest_fn as usize);
// and use it to call the corresponding function
let result = ctx.call_with_table_index(guest_fn_typed, &[]).unwrap();
println!("Guest fn {} returned {:?}", guest_fn, result);
0
}
fn main() {
let wasm_bytes =
std::fs::read(WASM).expect(&format!("Could not read in WASM plugin at {}", WASM));
let imports = imports! {
"env" => {
"call_guest_fn" => func!(call_guest_fn),
"call_guest_fn2" => func!(call_guest_fn),
"host_callback" => func!(host_callback),
},
};
let compiler = compiler_for_backend(Backend::default()).unwrap();
let module = compile_with(&wasm_bytes[..], compiler.as_ref()).unwrap();
let instance = module
.instantiate(&imports)
.expect("failed to instantiate wasm module");
let entry_point = instance.func::<(u32, u32), u32>("main").unwrap();
entry_point.call(0, 0).expect("START");
}

BIN
examples/parallel-guest.wasm Executable file

Binary file not shown.

View File

@ -0,0 +1,11 @@
[package]
name = "parallel-guest"
version = "0.1.0"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
license = "MIT"
edition = "2018"
publish = false
[dependencies]
md5 = "0.6"
lazy_static = "1"

View File

@ -0,0 +1,82 @@
#[macro_use]
extern crate lazy_static;
extern "C" {
fn get_hashed_password(ptr: u32, len: u32) -> u32;
fn print_char(c: u32);
}
fn print_str(s: &str) {
for c in s.chars() {
unsafe { print_char(c as u32) };
}
unsafe { print_char(b'\n' as u32) };
}
fn load_hashed_password() -> Option<String> {
let mut buffer = String::with_capacity(32);
for _ in 0..32 {
buffer.push(0 as char);
}
let result =
unsafe { get_hashed_password(buffer.as_mut_ptr() as u32, buffer.capacity() as u32) };
if result == 0 {
Some(buffer)
} else {
None
}
}
lazy_static! {
static ref HASHED_PASSWORD: String = load_hashed_password().unwrap();
}
static PASSWORD_CHARS: &'static [u8] = b"abcdefghijklmnopqrstuvwxyz0123456789";
// for simplicty we define a scheme for mapping numbers onto passwords
fn num_to_password(mut num: u64) -> String {
let mut extra_zero = num == 0;
let mut out = String::new();
while num > 0 {
out.push(PASSWORD_CHARS[num as usize % PASSWORD_CHARS.len()] as char);
extra_zero = extra_zero || num == PASSWORD_CHARS.len() as u64;
num /= PASSWORD_CHARS.len() as u64;
}
if extra_zero {
out.push(PASSWORD_CHARS[0] as char);
}
out
}
#[repr(C)]
struct RetStr {
ptr: u32,
len: u32,
}
// returns a (pointer, len) to the password or null
#[no_mangle]
fn check_password(from: u64, to: u64) -> u64 {
for i in from..to {
let password = num_to_password(i);
let digest = md5::compute(&password);
let hash_as_str = format!("{:x}", digest);
if hash_as_str == *HASHED_PASSWORD {
let ret = RetStr {
ptr: password.as_ptr() as usize as u32,
len: password.len() as u32,
};
// leak the data so ending the function doesn't corrupt it, if we cared the host could free it after
std::mem::forget(password);
return unsafe { std::mem::transmute(ret) };
}
}
return 0;
}
fn main() {}

View File

@ -0,0 +1,14 @@
[package]
name = "parallel"
version = "0.1.0"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
edition = "2018"
repository = "https://github.com/wasmerio/wasmer"
publish = false
license = "MIT"
[dependencies]
rayon = "1.2"
time = "0.1"
wasmer-runtime = { path = "../../lib/runtime" }
wasmer-runtime-core = { path = "../../lib/runtime-core" }

View File

@ -0,0 +1,5 @@
# Parallel Wasmer example
This example shows executing independent code from multiple threads on an "embarassingly parallel" problem
This is a toy example of cracking md5 hashes. This is not a benchmark. This example is not optimized, it will compare poorly to an implementation that is.

View File

@ -0,0 +1,137 @@
use rayon::prelude::*;
use wasmer_runtime::{compile_with, compiler_for_backend, func, imports, instantiate, Backend};
use wasmer_runtime_core::{
memory::ptr::{Array, WasmPtr},
vm::Ctx,
};
static PLUGIN_LOCATION: &'static str = "../parallel-guest.wasm";
fn get_hashed_password(ctx: &mut Ctx, ptr: WasmPtr<u8, Array>, len: u32) -> u32 {
// "hard" password - 7 characters
//let password = b"2ab96390c7dbe3439de74d0c9b0b1767";
// "easy" password - 5 characters
let password = b"ab56b4d92b40713acc5af89985d4b786";
let memory = ctx.memory(0);
if let Some(writer) = ptr.deref(memory, 0, len) {
for (i, byte) in password.iter().enumerate() {
writer[i].set(*byte)
}
0
} else {
u32::max_value()
}
}
#[repr(C)]
struct RetStr {
ptr: u32,
len: u32,
}
fn print_char(_cxt: &mut Ctx, c: u32) {
print!("{}", c as u8 as char);
}
fn main() {
let wasm_bytes = std::fs::read(PLUGIN_LOCATION).expect(&format!(
"Could not read in WASM plugin at {}",
PLUGIN_LOCATION
));
let imports = imports! {
"env" => {
"get_hashed_password" => func!(get_hashed_password),
"print_char" => func!(print_char),
},
};
let compiler = compiler_for_backend(Backend::default()).unwrap();
let module = compile_with(&wasm_bytes[..], compiler.as_ref()).unwrap();
println!("Parallel");
let start_ts = time::SteadyTime::now();
for outer in 0..1000u64 {
let start = outer * 1000;
let end = start + 1000;
let out = (start..=end)
.into_par_iter()
.filter_map(|i| {
let instance = module
.clone()
.instantiate(&imports)
.expect("failed to instantiate wasm module");
let check_password = instance.func::<(u64, u64), u64>("check_password").unwrap();
let j = i * 10000;
let result = check_password.call(j, j + 10000).unwrap();
print!(".");
use std::io::Write;
std::io::stdout().flush().unwrap();
if result != 0 {
let res: RetStr = unsafe { std::mem::transmute(result) };
let ctx = instance.context();
let memory = ctx.memory(0);
let wasm_ptr: WasmPtr<u8, Array> = WasmPtr::new(res.ptr);
let password_str = wasm_ptr
.get_utf8_string(memory, res.len)
.unwrap()
.to_string();
Some(password_str)
} else {
None
}
})
.find_first(|_: &String| true);
if out.is_some() {
let end_ts = time::SteadyTime::now();
let delta = end_ts - start_ts;
println!(
"Password cracked: \"{}\" in {}.{:03}",
out.unwrap(),
delta.num_seconds(),
(delta.num_milliseconds() % 1000),
);
break;
}
}
println!("Serial:");
let start_ts = time::SteadyTime::now();
let instance =
instantiate(&wasm_bytes[..], &imports).expect("failed to instantiate wasm module");
let check_password = instance.func::<(u64, u64), u64>("check_password").unwrap();
let mut out: Option<RetStr> = None;
for i in (0..=u64::max_value()).step_by(10000) {
let result = check_password.call(i, i + 10000).unwrap();
print!(".");
use std::io::Write;
std::io::stdout().flush().unwrap();
if result != 0 {
out = Some(unsafe { std::mem::transmute(result) });
break;
}
}
println!("");
if let Some(res) = out {
let ctx = instance.context();
let memory = ctx.memory(0);
let wasm_ptr: WasmPtr<u8, Array> = WasmPtr::new(res.ptr);
let password_str = wasm_ptr.get_utf8_string(memory, res.len).unwrap();
let end_ts = time::SteadyTime::now();
let delta = end_ts - start_ts;
println!(
"Password cracked: \"{}\" in {}.{:03}",
password_str,
delta.num_seconds(),
(delta.num_milliseconds() % 1000),
);
} else {
println!("Password not found!");
}
}

View File

@ -3,7 +3,7 @@ use wasmer_runtime::{func, imports, instantiate};
use wasmer_runtime_core::vm::Ctx; use wasmer_runtime_core::vm::Ctx;
use wasmer_wasi::{ use wasmer_wasi::{
generate_import_object, generate_import_object,
state::{self, WasiFile}, state::{self, WasiFile, WasiFsError},
types, types,
}; };
@ -102,6 +102,16 @@ impl WasiFile for LoggingWrapper {
fn size(&self) -> u64 { fn size(&self) -> u64 {
0 0
} }
fn set_len(&mut self, _len: u64) -> Result<(), WasiFsError> {
Ok(())
}
fn unlink(&mut self) -> Result<(), WasiFsError> {
Ok(())
}
fn bytes_available(&self) -> Result<usize, WasiFsError> {
// return an arbitrary amount
Ok(1024)
}
} }
/// Called by the program when it wants to set itself up /// Called by the program when it wants to set itself up

View File

@ -12,6 +12,8 @@ cargo-fuzz = true
wasmer-runtime = { path = "../lib/runtime" } wasmer-runtime = { path = "../lib/runtime" }
wasmer-runtime-core = { path = "../lib/runtime-core" } wasmer-runtime-core = { path = "../lib/runtime-core" }
wasmer = { path = "../" } wasmer = { path = "../" }
wasmer-llvm-backend = { path = "../lib/llvm-backend" }
wasmer-singlepass-backend = { path = "../lib/singlepass-backend" }
libfuzzer-sys = { git = "https://github.com/rust-fuzz/libfuzzer-sys.git" } libfuzzer-sys = { git = "https://github.com/rust-fuzz/libfuzzer-sys.git" }
# Prevent this from interfering with workspaces # Prevent this from interfering with workspaces
@ -25,3 +27,7 @@ path = "fuzz_targets/simple_instantiate.rs"
[[bin]] [[bin]]
name = "validate_wasm" name = "validate_wasm"
path = "fuzz_targets/validate_wasm.rs" path = "fuzz_targets/validate_wasm.rs"
[[bin]]
name = "compile_wasm"
path = "fuzz_targets/compile_wasm.rs"

View File

@ -10,7 +10,7 @@ $ cargo install cargo-fuzz
`cargo-fuzz` is documented in the [Rust Fuzz Book](https://rust-fuzz.github.io/book/cargo-fuzz.html). `cargo-fuzz` is documented in the [Rust Fuzz Book](https://rust-fuzz.github.io/book/cargo-fuzz.html).
## Running a fuzzer (simple_instantiate, validate_wasm) ## Running a fuzzer (simple_instantiate, validate_wasm, compile_wasm)
Once `cargo-fuzz` is installed, you can run the `simple_instantiate` fuzzer with Once `cargo-fuzz` is installed, you can run the `simple_instantiate` fuzzer with
```sh ```sh
@ -20,6 +20,10 @@ or the `validate_wasm` fuzzer
```sh ```sh
cargo fuzz run validate_wasm cargo fuzz run validate_wasm
``` ```
or the `compile_wasm` fuzzer
```sh
cargo fuzz run compile_wasm
```
You should see output that looks something like this: You should see output that looks something like this:
@ -43,7 +47,7 @@ The fuzzer works best when it has examples of small Wasm files to start with. Us
```sh ```sh
mkdir spec-test-corpus mkdir spec-test-corpus
for i in lib/spectests/spectests/*.wast; do wast2json $i -o spec-test-corpus/$(basename $i).json; done for i in lib/spectests/spectests/*.wast; do wast2json --enable-all $i -o spec-test-corpus/$(basename $i).json; done
mv spec-test-corpus/*.wasm fuzz/corpus/simple_instantiate/ mv spec-test-corpus/*.wasm fuzz/corpus/simple_instantiate/
rm -r spec-test-corpus rm -r spec-test-corpus
``` ```

View File

@ -0,0 +1,25 @@
#![no_main]
#[macro_use]
extern crate libfuzzer_sys;
extern crate wasmer_runtime;
extern crate wasmer_runtime_core;
extern crate wasmer_llvm_backend;
extern crate wasmer_singlepass_backend;
use wasmer_runtime::{compile, compile_with};
use wasmer_runtime_core::backend::Compiler;
fn get_llvm_compiler() -> impl Compiler {
use wasmer_llvm_backend::LLVMCompiler;
LLVMCompiler::new()
}
fn get_singlepass_compiler() -> impl Compiler {
use wasmer_singlepass_backend::SinglePassCompiler;
SinglePassCompiler::new()
}
fuzz_target!(|data: &[u8]| {
let _ = compile_with(data, &get_llvm_compiler());
let _ = compile(data);
let _ = compile_with(data, &get_singlepass_compiler());
});

View File

@ -1,8 +1,8 @@
#!/bin/sh #!/bin/sh
# This install script is intended to download and install the latest available # This install script is intended to download and install the latest available
# release of the wasmer. # release of Wasmer.
# Installer script inspired from: # Installer script inspired by:
# 1) https://raw.githubusercontent.com/golang/dep/master/install.sh # 1) https://raw.githubusercontent.com/golang/dep/master/install.sh
# 2) https://sh.rustup.rs # 2) https://sh.rustup.rs
# 3) https://yarnpkg.com/install.sh # 3) https://yarnpkg.com/install.sh
@ -22,7 +22,6 @@
set -e set -e
reset="\033[0m" reset="\033[0m"
red="\033[31m" red="\033[31m"
green="\033[32m" green="\033[32m"
@ -257,10 +256,10 @@ wasmer_install() {
printf "${reset}Installing Wasmer and WAPM!$reset\n" printf "${reset}Installing Wasmer and WAPM!$reset\n"
if [ "$WASMER_INSTALL_LOG" = "$WASMER_VERBOSE" ]; then if [ "$WASMER_INSTALL_LOG" = "$WASMER_VERBOSE" ]; then
printf " printf "
${magenta1} ww ${magenta1} ww
${magenta1} wwwww ${magenta1} wwwww
${magenta1} ww wwwwww w ${magenta1} ww wwwwww w
${magenta1} wwwww wwwwwwwww ${magenta1} wwwww wwwwwwwww
${magenta1}ww wwwwww w wwwwwww ${magenta1}ww wwwwww w wwwwwww
${magenta1}wwwww wwwwwwwwww wwwww ${magenta1}wwwww wwwwwwwwww wwwww
${magenta1}wwwwww w wwwwwww wwwww ${magenta1}wwwwww w wwwwwww wwwww
@ -269,10 +268,10 @@ ${magenta1}wwwwwwwwwwwwwww wwwww wwwww
${magenta1}wwwwwwwwwwwwwww wwwww wwwww ${magenta1}wwwwwwwwwwwwwww wwwww wwwww
${magenta1}wwwwwwwwwwwwwww wwwww wwwww ${magenta1}wwwwwwwwwwwwwww wwwww wwwww
${magenta1}wwwwwwwwwwwwwww wwwww wwww ${magenta1}wwwwwwwwwwwwwww wwwww wwww
${magenta1}wwwwwwwwwwwwwww wwwww ${magenta1}wwwwwwwwwwwwwww wwwww
${magenta1} wwwwwwwwwwww wwww ${magenta1} wwwwwwwwwwww wwww
${magenta1} wwwwwwww ${magenta1} wwwwwwww
${magenta1} wwww ${magenta1} wwww
${reset} ${reset}
" "
fi fi

View File

@ -1,9 +1,8 @@
# `cowsay` integration test # `cowsay` integration test
This starts Wasmer with the Cowsay WASI Wasm file. The test makes assertions on This starts Wasmer with the Cowsay WASI Wasm file. The test makes assertions on
the output of Wasmer. Run test with: the output of Wasmer. Run test with:
```bash ```sh
./integration_tests/cowsay/test.sh ./integration_tests/cowsay/test.sh
``` ```

View File

@ -1,6 +1,6 @@
[package] [package]
name = "wasmer-clif-backend" name = "wasmer-clif-backend"
version = "0.6.0" version = "0.9.0"
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>"]
@ -9,33 +9,33 @@ edition = "2018"
readme = "README.md" readme = "README.md"
[dependencies] [dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0" } wasmer-runtime-core = { path = "../runtime-core", version = "0.9.0" }
cranelift-native = { version = "0.31" } cranelift-native = "0.44.0"
cranelift-codegen = { version = "0.31" } cranelift-codegen = "0.44.0"
cranelift-entity = { version = "0.31" } cranelift-entity = "0.44.0"
cranelift-frontend = { package = "wasmer-clif-fork-frontend", version = "0.33" } cranelift-frontend = { package = "wasmer-clif-fork-frontend", version = "0.44.0" }
cranelift-wasm = { package = "wasmer-clif-fork-wasm", version = "0.33" } cranelift-wasm = { package = "wasmer-clif-fork-wasm", version = "0.44.0" }
target-lexicon = "0.4.0" target-lexicon = "0.8.1"
wasmparser = "0.35.1" wasmparser = "0.39.1"
byteorder = "1.3.2" byteorder = "1.3.2"
nix = "0.15.0" nix = "0.15.0"
libc = "0.2.60" libc = "0.2.60"
rayon = "1.1.0" rayon = "1.1"
# Dependencies for caching. # Dependencies for caching.
[dependencies.serde] [dependencies.serde]
version = "1.0.99" version = "1.0"
features = ["rc"] features = ["rc"]
[dependencies.serde_derive] [dependencies.serde_derive]
version = "1.0.98" version = "1.0"
[dependencies.serde_bytes] [dependencies.serde_bytes]
version = "0.11.2" version = "0.11"
[dependencies.serde-bench] [dependencies.serde-bench]
version = "0.0.7" version = "0.0.7"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.8", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] } winapi = { version = "0.3", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] }
wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.6.0" } wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.9.0" }
[features] [features]
debug = ["wasmer-runtime-core/debug"] debug = ["wasmer-runtime-core/debug"]

View File

@ -1,21 +1,21 @@
<p align="center"> <p align="center">
<a href="https://wasmer.io" target="_blank" rel="noopener noreferrer"> <a href="https://wasmer.io" target="_blank" rel="noopener noreferrer">
<img width="400" src="https://raw.githubusercontent.com/wasmerio/wasmer/master/logo.png" alt="Wasmer logo"> <img width="300" src="https://raw.githubusercontent.com/wasmerio/wasmer/master/logo.png" alt="Wasmer logo">
</a> </a>
</p> </p>
<p align="center"> <p align="center">
<a href="https://circleci.com/gh/wasmerio/wasmer/"> <a href="https://dev.azure.com/wasmerio/wasmer/_build/latest?definitionId=3&branchName=master">
<img src="https://img.shields.io/circleci/project/github/wasmerio/wasmer/master.svg" alt="Build Status"> <img src="https://img.shields.io/azure-devops/build/wasmerio/wasmer/3.svg?style=flat-square" alt="Build Status">
</a> </a>
<a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE"> <a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/wasmerio/wasmer.svg" alt="License"> <img src="https://img.shields.io/github/license/wasmerio/wasmer.svg?style=flat-square" alt="License">
</a> </a>
<a href="https://spectrum.chat/wasmer"> <a href="https://spectrum.chat/wasmer">
<img src="https://withspectrum.github.io/badge/badge.svg" alt="Join the Wasmer Community"> <img src="https://withspectrum.github.io/badge/badge.svg" alt="Join the Wasmer Community">
</a> </a>
<a href="https://crates.io/crates/wasmer-clif-backend"> <a href="https://crates.io/crates/wasmer-clif-backend">
<img src="https://img.shields.io/crates/d/wasmer-clif-backend.svg" alt="Number of downloads from crates.io"> <img src="https://img.shields.io/crates/d/wasmer-clif-backend.svg?style=flat-square" alt="Number of downloads from crates.io">
</a> </a>
<a href="https://docs.rs/wasmer-clif-backend"> <a href="https://docs.rs/wasmer-clif-backend">
<img src="https://docs.rs/wasmer-clif-backend/badge.svg" alt="Read our API documentation"> <img src="https://docs.rs/wasmer-clif-backend/badge.svg" alt="Read our API documentation">
@ -36,7 +36,7 @@ This crate represents the Cranelift backend integration for Wasmer.
If you are using the `wasmer` CLI, you can specify the backend with: If you are using the `wasmer` CLI, you can specify the backend with:
```bash ```sh
wasmer run program.wasm --backend=cranelift wasmer run program.wasm --backend=cranelift
``` ```

View File

@ -128,166 +128,6 @@ impl ModuleCodeGenerator<CraneliftFunctionCodeGenerator, Caller, CodegenError>
.state .state
.initialize(&builder.func.signature, exit_block); .initialize(&builder.func.signature, exit_block);
#[cfg(feature = "debug")]
{
use cranelift_codegen::cursor::{Cursor, FuncCursor};
use cranelift_codegen::ir::InstBuilder;
let entry_ebb = func.layout.entry_block().unwrap();
let ebb = func.dfg.make_ebb();
func.layout.insert_ebb(ebb, entry_ebb);
let mut pos = FuncCursor::new(&mut func).at_first_insertion_point(ebb);
let params = pos.func.dfg.ebb_params(entry_ebb).to_vec();
let new_ebb_params: Vec<_> = params
.iter()
.map(|&param| {
pos.func
.dfg
.append_ebb_param(ebb, pos.func.dfg.value_type(param))
})
.collect();
let start_debug = {
let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv,
params: vec![
ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext),
ir::AbiParam::new(ir::types::I32),
],
returns: vec![],
});
let name = ir::ExternalName::testcase("strtdbug");
pos.func.import_function(ir::ExtFuncData {
name,
signature,
colocated: false,
})
};
let end_debug = {
let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv,
params: vec![ir::AbiParam::special(
ir::types::I64,
ir::ArgumentPurpose::VMContext,
)],
returns: vec![],
});
let name = ir::ExternalName::testcase("enddbug");
pos.func.import_function(ir::ExtFuncData {
name,
signature,
colocated: false,
})
};
let i32_print = {
let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv,
params: vec![
ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext),
ir::AbiParam::new(ir::types::I32),
],
returns: vec![],
});
let name = ir::ExternalName::testcase("i32print");
pos.func.import_function(ir::ExtFuncData {
name,
signature,
colocated: false,
})
};
let i64_print = {
let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv,
params: vec![
ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext),
ir::AbiParam::new(ir::types::I64),
],
returns: vec![],
});
let name = ir::ExternalName::testcase("i64print");
pos.func.import_function(ir::ExtFuncData {
name,
signature,
colocated: false,
})
};
let f32_print = {
let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv,
params: vec![
ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext),
ir::AbiParam::new(ir::types::F32),
],
returns: vec![],
});
let name = ir::ExternalName::testcase("f32print");
pos.func.import_function(ir::ExtFuncData {
name,
signature,
colocated: false,
})
};
let f64_print = {
let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv,
params: vec![
ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext),
ir::AbiParam::new(ir::types::F64),
],
returns: vec![],
});
let name = ir::ExternalName::testcase("f64print");
pos.func.import_function(ir::ExtFuncData {
name,
signature,
colocated: false,
})
};
let vmctx = pos
.func
.special_param(ir::ArgumentPurpose::VMContext)
.expect("missing vmctx parameter");
let func_index = pos.ins().iconst(
ir::types::I32,
func_index.index() as i64 + self.module.info.imported_functions.len() as i64,
);
pos.ins().call(start_debug, &[vmctx, func_index]);
for param in new_ebb_params.iter().cloned() {
match pos.func.dfg.value_type(param) {
ir::types::I32 => pos.ins().call(i32_print, &[vmctx, param]),
ir::types::I64 => pos.ins().call(i64_print, &[vmctx, param]),
ir::types::F32 => pos.ins().call(f32_print, &[vmctx, param]),
ir::types::F64 => pos.ins().call(f64_print, &[vmctx, param]),
_ => unimplemented!(),
};
}
pos.ins().call(end_debug, &[vmctx]);
pos.ins().jump(entry_ebb, new_ebb_params.as_slice());
}
self.functions.push(func_env); self.functions.push(func_env);
Ok(self.functions.last_mut().unwrap()) Ok(self.functions.last_mut().unwrap())
} }
@ -1117,7 +957,18 @@ impl FunctionCodeGenerator<CodegenError> for CraneliftFunctionCodeGenerator {
fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), CodegenError> { fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), CodegenError> {
let mut next_local = self.next_local; let mut next_local = self.next_local;
cranelift_wasm::declare_locals(&mut self.builder(), n as u32, ty, &mut next_local)?; let mut builder = FunctionBuilder::new(
&mut self.func,
&mut self.func_translator.func_ctx,
&mut self.position,
);
cranelift_wasm::declare_locals(
&mut builder,
n as u32,
ty,
&mut next_local,
&mut self.func_env,
)?;
self.next_local = next_local; self.next_local = next_local;
Ok(()) Ok(())
} }

View File

@ -34,7 +34,7 @@ extern crate serde;
fn get_isa() -> Box<dyn isa::TargetIsa> { fn get_isa() -> Box<dyn isa::TargetIsa> {
let flags = { let flags = {
let mut builder = settings::builder(); let mut builder = settings::builder();
builder.set("opt_level", "best").unwrap(); builder.set("opt_level", "speed_and_size").unwrap();
builder.set("jump_tables_enabled", "false").unwrap(); builder.set("jump_tables_enabled", "false").unwrap();
if cfg!(not(test)) { if cfg!(not(test)) {
@ -42,7 +42,7 @@ fn get_isa() -> Box<dyn isa::TargetIsa> {
} }
let flags = settings::Flags::new(builder); let flags = settings::Flags::new(builder);
debug_assert_eq!(flags.opt_level(), settings::OptLevel::Best); debug_assert_eq!(flags.opt_level(), settings::OptLevel::SpeedAndSize);
flags flags
}; };
isa::lookup(Triple::host()).unwrap().finish(flags) isa::lookup(Triple::host()).unwrap().finish(flags)

View File

@ -105,7 +105,7 @@ impl binemit::RelocSink for RelocSink {
_ebb_offset: binemit::CodeOffset, _ebb_offset: binemit::CodeOffset,
) { ) {
// This should use the `offsets` field of `ir::Function`. // This should use the `offsets` field of `ir::Function`.
unimplemented!(); unimplemented!("RelocSink::reloc_ebb");
} }
fn reloc_external( fn reloc_external(
&mut self, &mut self,
@ -146,7 +146,7 @@ impl binemit::RelocSink for RelocSink {
DYNAMIC_MEM_GROW => VmCallKind::DynamicMemoryGrow, DYNAMIC_MEM_GROW => VmCallKind::DynamicMemoryGrow,
DYNAMIC_MEM_SIZE => VmCallKind::DynamicMemorySize, DYNAMIC_MEM_SIZE => VmCallKind::DynamicMemorySize,
_ => unimplemented!(), _ => unimplemented!("reloc_external VmCall::Local {}", index),
})), })),
IMPORT_NAMESPACE => RelocationType::VmCall(VmCall::Import(match index { IMPORT_NAMESPACE => RelocationType::VmCall(VmCall::Import(match index {
STATIC_MEM_GROW => VmCallKind::StaticMemoryGrow, STATIC_MEM_GROW => VmCallKind::StaticMemoryGrow,
@ -157,10 +157,10 @@ impl binemit::RelocSink for RelocSink {
DYNAMIC_MEM_GROW => VmCallKind::DynamicMemoryGrow, DYNAMIC_MEM_GROW => VmCallKind::DynamicMemoryGrow,
DYNAMIC_MEM_SIZE => VmCallKind::DynamicMemorySize, DYNAMIC_MEM_SIZE => VmCallKind::DynamicMemorySize,
_ => unimplemented!(), _ => unimplemented!("reloc_external VmCall::Import {}", index),
})), })),
SIG_NAMESPACE => RelocationType::Signature(SigIndex::new(index as usize)), SIG_NAMESPACE => RelocationType::Signature(SigIndex::new(index as usize)),
_ => unimplemented!(), _ => unimplemented!("reloc_external SigIndex {}", index),
}; };
self.external_relocs.push(ExternalRelocation { self.external_relocs.push(ExternalRelocation {
reloc, reloc,
@ -202,13 +202,18 @@ impl binemit::RelocSink for RelocSink {
} }
} }
} }
fn reloc_constant(&mut self, _: u32, _: cranelift_codegen::binemit::Reloc, _: u32) {
unimplemented!("RelocSink::reloc_constant")
}
fn reloc_jt( fn reloc_jt(
&mut self, &mut self,
_offset: binemit::CodeOffset, _offset: binemit::CodeOffset,
_reloc: binemit::Reloc, _reloc: binemit::Reloc,
_jt: ir::JumpTable, _jt: ir::JumpTable,
) { ) {
unimplemented!(); unimplemented!("RelocSink::reloc_jt");
} }
} }

View File

@ -1,29 +1,31 @@
use crate::{cache::BackendCache, trampoline::Trampolines};
use crate::{ use crate::{
cache::BackendCache,
libcalls, libcalls,
relocation::{ relocation::{
ExternalRelocation, LibCall, LocalRelocation, LocalTrapSink, Reloc, RelocSink, ExternalRelocation, LibCall, LocalRelocation, LocalTrapSink, Reloc, RelocSink,
RelocationType, TrapSink, VmCall, VmCallKind, RelocationType, TrapSink, VmCall, VmCallKind,
}, },
signal::HandlerData, signal::HandlerData,
trampoline::Trampolines,
};
use byteorder::{ByteOrder, LittleEndian};
use cranelift_codegen::{
binemit::{Stackmap, StackmapSink},
ir, isa, Context,
}; };
use rayon::prelude::*; use rayon::prelude::*;
use byteorder::{ByteOrder, LittleEndian};
use cranelift_codegen::{ir, isa, Context};
use std::{ use std::{
mem, mem,
ptr::{write_unaligned, NonNull}, ptr::{write_unaligned, NonNull},
sync::Arc, sync::Arc,
}; };
use wasmer_runtime_core::cache::Error as CacheError;
use wasmer_runtime_core::{ use wasmer_runtime_core::{
self, self,
backend::{ backend::{
sys::{Memory, Protect}, sys::{Memory, Protect},
SigRegistry, SigRegistry,
}, },
cache::Error as CacheError,
error::{CompileError, CompileResult}, error::{CompileError, CompileResult},
module::ModuleInfo, module::ModuleInfo,
structures::{Map, SliceMap, TypedIndex}, structures::{Map, SliceMap, TypedIndex},
@ -58,6 +60,11 @@ pub struct FuncResolverBuilder {
import_len: usize, import_len: usize,
} }
pub struct NoopStackmapSink {}
impl StackmapSink for NoopStackmapSink {
fn add_stackmap(&mut self, _: u32, _: Stackmap) {}
}
impl FuncResolverBuilder { impl FuncResolverBuilder {
pub fn new_from_backend_cache( pub fn new_from_backend_cache(
backend_cache: BackendCache, backend_cache: BackendCache,
@ -109,12 +116,13 @@ impl FuncResolverBuilder {
ctx.func = func.to_owned(); ctx.func = func.to_owned();
let mut reloc_sink = RelocSink::new(); let mut reloc_sink = RelocSink::new();
let mut local_trap_sink = LocalTrapSink::new(); let mut local_trap_sink = LocalTrapSink::new();
let mut stackmap_sink = NoopStackmapSink {};
ctx.compile_and_emit( ctx.compile_and_emit(
isa, isa,
&mut code_buf, &mut code_buf,
&mut reloc_sink, &mut reloc_sink,
&mut local_trap_sink, &mut local_trap_sink,
&mut stackmap_sink,
) )
.map_err(|e| CompileError::InternalError { msg: e.to_string() })?; .map_err(|e| CompileError::InternalError { msg: e.to_string() })?;
ctx.clear(); ctx.clear();
@ -241,25 +249,17 @@ impl FuncResolverBuilder {
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
LibCall::Probestack => __rust_probestack as isize, LibCall::Probestack => __rust_probestack as isize,
}, },
RelocationType::Intrinsic(ref name) => match name.as_str() { RelocationType::Intrinsic(ref name) => Err(CompileError::InternalError {
"i32print" => i32_print as isize, msg: format!("unexpected intrinsic: {}", name),
"i64print" => i64_print as isize, })?,
"f32print" => f32_print as isize,
"f64print" => f64_print as isize,
"strtdbug" => start_debug as isize,
"enddbug" => end_debug as isize,
_ => Err(CompileError::InternalError {
msg: format!("unexpected intrinsic: {}", name),
})?,
},
RelocationType::VmCall(vmcall) => match vmcall { RelocationType::VmCall(vmcall) => match vmcall {
VmCall::Local(kind) => match kind { VmCall::Local(kind) => match kind {
VmCallKind::StaticMemoryGrow => vmcalls::local_static_memory_grow as _, VmCallKind::StaticMemoryGrow | VmCallKind::SharedStaticMemoryGrow => {
VmCallKind::StaticMemorySize => vmcalls::local_static_memory_size as _, vmcalls::local_static_memory_grow as _
}
VmCallKind::SharedStaticMemoryGrow => unimplemented!(), VmCallKind::StaticMemorySize | VmCallKind::SharedStaticMemorySize => {
VmCallKind::SharedStaticMemorySize => unimplemented!(), vmcalls::local_static_memory_size as _
}
VmCallKind::DynamicMemoryGrow => { VmCallKind::DynamicMemoryGrow => {
vmcalls::local_dynamic_memory_grow as _ vmcalls::local_dynamic_memory_grow as _
} }
@ -268,16 +268,12 @@ impl FuncResolverBuilder {
} }
}, },
VmCall::Import(kind) => match kind { VmCall::Import(kind) => match kind {
VmCallKind::StaticMemoryGrow => { VmCallKind::StaticMemoryGrow | VmCallKind::SharedStaticMemoryGrow => {
vmcalls::imported_static_memory_grow as _ vmcalls::imported_static_memory_grow as _
} }
VmCallKind::StaticMemorySize => { VmCallKind::StaticMemorySize | VmCallKind::SharedStaticMemorySize => {
vmcalls::imported_static_memory_size as _ vmcalls::imported_static_memory_size as _
} }
VmCallKind::SharedStaticMemoryGrow => unimplemented!(),
VmCallKind::SharedStaticMemorySize => unimplemented!(),
VmCallKind::DynamicMemoryGrow => { VmCallKind::DynamicMemoryGrow => {
vmcalls::imported_dynamic_memory_grow as _ vmcalls::imported_dynamic_memory_grow as _
} }
@ -366,28 +362,3 @@ impl FuncResolver {
fn round_up(n: usize, multiple: usize) -> usize { fn round_up(n: usize, multiple: usize) -> usize {
(n + multiple - 1) & !(multiple - 1) (n + multiple - 1) & !(multiple - 1)
} }
extern "C" fn i32_print(_ctx: &mut vm::Ctx, n: i32) {
eprint!(" i32: {},", n);
}
extern "C" fn i64_print(_ctx: &mut vm::Ctx, n: i64) {
eprint!(" i64: {},", n);
}
extern "C" fn f32_print(_ctx: &mut vm::Ctx, n: f32) {
eprint!(" f32: {},", n);
}
extern "C" fn f64_print(_ctx: &mut vm::Ctx, n: f64) {
eprint!(" f64: {},", n);
}
extern "C" fn start_debug(ctx: &mut vm::Ctx, func_index: u32) {
if let Some(symbol_map) = unsafe { ctx.borrow_symbol_map() } {
if let Some(fn_name) = symbol_map.get(&func_index) {
eprint!("func ({} ({})), args: [", fn_name, func_index);
return;
}
}
eprint!("func ({}), args: [", func_index);
}
extern "C" fn end_debug(_ctx: &mut vm::Ctx) {
eprintln!(" ]");
}

View File

@ -1,12 +1,14 @@
use crate::relocation::{TrapData, TrapSink}; use crate::{
use crate::resolver::FuncResolver; relocation::{TrapData, TrapSink},
use crate::trampoline::Trampolines; resolver::FuncResolver,
trampoline::Trampolines,
};
use libc::c_void; use libc::c_void;
use std::{any::Any, cell::Cell, ptr::NonNull, sync::Arc}; use std::{any::Any, cell::Cell, ptr::NonNull, sync::Arc};
use wasmer_runtime_core::{ use wasmer_runtime_core::{
backend::RunnableModule, backend::RunnableModule,
module::ModuleInfo, module::ModuleInfo,
typed_func::{Wasm, WasmTrapInfo}, typed_func::{Trampoline, Wasm, WasmTrapInfo},
types::{LocalFuncIndex, SigIndex}, types::{LocalFuncIndex, SigIndex},
vm, vm,
}; };
@ -59,7 +61,7 @@ impl RunnableModule for Caller {
fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option<Wasm> { fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option<Wasm> {
unsafe extern "C" fn invoke( unsafe extern "C" fn invoke(
trampoline: unsafe extern "C" fn(*mut vm::Ctx, NonNull<vm::Func>, *const u64, *mut u64), trampoline: Trampoline,
ctx: *mut vm::Ctx, ctx: *mut vm::Ctx,
func: NonNull<vm::Func>, func: NonNull<vm::Func>,
args: *const u64, args: *const u64,

View File

@ -98,7 +98,10 @@ pub fn call_protected<T>(
}, },
Ok(SIGSEGV) | Ok(SIGBUS) => WasmTrapInfo::MemoryOutOfBounds, Ok(SIGSEGV) | Ok(SIGBUS) => WasmTrapInfo::MemoryOutOfBounds,
Ok(SIGFPE) => WasmTrapInfo::IllegalArithmetic, Ok(SIGFPE) => WasmTrapInfo::IllegalArithmetic,
_ => unimplemented!(), _ => unimplemented!(
"WasmTrapInfo::Unknown signal:{:?}",
Signal::from_c_int(signum)
),
})) }))
} else { } else {
let signal = match Signal::from_c_int(signum) { let signal = match Signal::from_c_int(signum) {

View File

@ -1,24 +1,30 @@
use crate::relocation::{TrapCode, TrapData}; use crate::{
use crate::signal::{CallProtError, HandlerData}; relocation::{TrapCode, TrapData},
use crate::trampoline::Trampoline; signal::{CallProtError, HandlerData},
use std::cell::Cell; };
use std::ffi::c_void; use std::{
use std::ptr::{self, NonNull}; cell::Cell,
use wasmer_runtime_core::typed_func::WasmTrapInfo; ffi::c_void,
use wasmer_runtime_core::vm::Ctx; ptr::{self, NonNull},
use wasmer_runtime_core::vm::Func; };
use wasmer_runtime_core::{
typed_func::{Trampoline, WasmTrapInfo},
vm::{Ctx, Func},
};
use wasmer_win_exception_handler::CallProtectedData; use wasmer_win_exception_handler::CallProtectedData;
pub use wasmer_win_exception_handler::_call_protected; pub use wasmer_win_exception_handler::_call_protected;
use winapi::shared::minwindef::DWORD; use winapi::{
use winapi::um::minwinbase::{ shared::minwindef::DWORD,
EXCEPTION_ACCESS_VIOLATION, EXCEPTION_ARRAY_BOUNDS_EXCEEDED, EXCEPTION_BREAKPOINT, um::minwinbase::{
EXCEPTION_DATATYPE_MISALIGNMENT, EXCEPTION_FLT_DENORMAL_OPERAND, EXCEPTION_FLT_DIVIDE_BY_ZERO, EXCEPTION_ACCESS_VIOLATION, EXCEPTION_ARRAY_BOUNDS_EXCEEDED, EXCEPTION_BREAKPOINT,
EXCEPTION_FLT_INEXACT_RESULT, EXCEPTION_FLT_INVALID_OPERATION, EXCEPTION_FLT_OVERFLOW, EXCEPTION_DATATYPE_MISALIGNMENT, EXCEPTION_FLT_DENORMAL_OPERAND,
EXCEPTION_FLT_STACK_CHECK, EXCEPTION_FLT_UNDERFLOW, EXCEPTION_GUARD_PAGE, EXCEPTION_FLT_DIVIDE_BY_ZERO, EXCEPTION_FLT_INEXACT_RESULT,
EXCEPTION_ILLEGAL_INSTRUCTION, EXCEPTION_INT_DIVIDE_BY_ZERO, EXCEPTION_INT_OVERFLOW, EXCEPTION_FLT_INVALID_OPERATION, EXCEPTION_FLT_OVERFLOW, EXCEPTION_FLT_STACK_CHECK,
EXCEPTION_INVALID_HANDLE, EXCEPTION_IN_PAGE_ERROR, EXCEPTION_NONCONTINUABLE_EXCEPTION, EXCEPTION_FLT_UNDERFLOW, EXCEPTION_GUARD_PAGE, EXCEPTION_ILLEGAL_INSTRUCTION,
EXCEPTION_POSSIBLE_DEADLOCK, EXCEPTION_PRIV_INSTRUCTION, EXCEPTION_SINGLE_STEP, EXCEPTION_INT_DIVIDE_BY_ZERO, EXCEPTION_INT_OVERFLOW, EXCEPTION_INVALID_HANDLE,
EXCEPTION_STACK_OVERFLOW, EXCEPTION_IN_PAGE_ERROR, EXCEPTION_NONCONTINUABLE_EXCEPTION, EXCEPTION_POSSIBLE_DEADLOCK,
EXCEPTION_PRIV_INSTRUCTION, EXCEPTION_SINGLE_STEP, EXCEPTION_STACK_OVERFLOW,
},
}; };
thread_local! { thread_local! {
@ -110,5 +116,5 @@ pub fn call_protected(
pub unsafe fn trigger_trap() -> ! { pub unsafe fn trigger_trap() -> ! {
// TODO // TODO
unimplemented!(); unimplemented!("windows::trigger_trap");
} }

View File

@ -1,17 +1,16 @@
use crate::cache::TrampolineCache; use crate::{cache::TrampolineCache, resolver::NoopStackmapSink};
use cranelift_codegen::{ use cranelift_codegen::{
binemit::{NullTrapSink, Reloc, RelocSink}, binemit::{NullTrapSink, Reloc, RelocSink},
cursor::{Cursor, FuncCursor}, cursor::{Cursor, FuncCursor},
ir::{self, InstBuilder}, ir::{self, InstBuilder},
isa, Context, isa, Context,
}; };
use std::collections::HashMap; use std::{collections::HashMap, iter, mem};
use std::{iter, mem, ptr::NonNull};
use wasmer_runtime_core::{ use wasmer_runtime_core::{
backend::sys::{Memory, Protect}, backend::sys::{Memory, Protect},
module::{ExportIndex, ModuleInfo}, module::{ExportIndex, ModuleInfo},
typed_func::Trampoline,
types::{FuncSig, SigIndex, Type}, types::{FuncSig, SigIndex, Type},
vm,
}; };
struct NullRelocSink {} struct NullRelocSink {}
@ -19,11 +18,14 @@ struct NullRelocSink {}
impl RelocSink for NullRelocSink { impl RelocSink for NullRelocSink {
fn reloc_ebb(&mut self, _: u32, _: Reloc, _: u32) {} fn reloc_ebb(&mut self, _: u32, _: Reloc, _: u32) {}
fn reloc_external(&mut self, _: u32, _: Reloc, _: &ir::ExternalName, _: i64) {} fn reloc_external(&mut self, _: u32, _: Reloc, _: &ir::ExternalName, _: i64) {}
fn reloc_constant(&mut self, _: u32, _: Reloc, _: u32) {
unimplemented!("RelocSink::reloc_constant")
}
fn reloc_jt(&mut self, _: u32, _: Reloc, _: ir::JumpTable) {} fn reloc_jt(&mut self, _: u32, _: Reloc, _: ir::JumpTable) {}
} }
pub type Trampoline = unsafe extern "C" fn(*mut vm::Ctx, NonNull<vm::Func>, *const u64, *mut u64);
pub struct Trampolines { pub struct Trampolines {
memory: Memory, memory: Memory,
offsets: HashMap<SigIndex, usize>, offsets: HashMap<SigIndex, usize>,
@ -89,12 +91,13 @@ impl Trampolines {
ctx.func = trampoline_func; ctx.func = trampoline_func;
let mut code_buf = Vec::new(); let mut code_buf = Vec::new();
let mut stackmap_sink = NoopStackmapSink {};
ctx.compile_and_emit( ctx.compile_and_emit(
isa, isa,
&mut code_buf, &mut code_buf,
&mut NullRelocSink {}, &mut NullRelocSink {},
&mut NullTrapSink {}, &mut NullTrapSink {},
&mut stackmap_sink,
) )
.expect("unable to compile trampolines"); .expect("unable to compile trampolines");
ctx.clear(); ctx.clear();

View File

@ -1,6 +1,6 @@
[package] [package]
name = "wasmer-dev-utils" name = "wasmer-dev-utils"
version = "0.6.0" version = "0.9.0"
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>"]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "wasmer-emscripten-tests" name = "wasmer-emscripten-tests"
version = "0.6.0" version = "0.9.0"
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,18 +9,18 @@ publish = false
build = "build/mod.rs" build = "build/mod.rs"
[dependencies] [dependencies]
wasmer-emscripten = { path = "../emscripten", version = "0.6.0" } wasmer-emscripten = { path = "../emscripten", version = "0.9.0" }
wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0" } wasmer-runtime-core = { path = "../runtime-core", version = "0.9.0" }
wasmer-clif-backend = { path = "../clif-backend", version = "0.6.0" } wasmer-clif-backend = { path = "../clif-backend", version = "0.9.0" }
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.6.0", optional = true } wasmer-llvm-backend = { path = "../llvm-backend", version = "0.9.0", optional = true }
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.6.0", optional = true } wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.9.0", optional = true }
[dev-dependencies] [dev-dependencies]
wabt = "0.9.1" wabt = "0.9.1"
wasmer-dev-utils = { path = "../dev-utils", version = "0.6.0"} wasmer-dev-utils = { path = "../dev-utils", version = "0.9.0"}
[build-dependencies] [build-dependencies]
glob = "0.3.0" glob = "0.3"
[features] [features]
clif = [] clif = []

View File

@ -1,6 +1,6 @@
[package] [package]
name = "wasmer-emscripten" name = "wasmer-emscripten"
version = "0.6.0" version = "0.9.0"
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>"]
@ -8,14 +8,14 @@ repository = "https://github.com/wasmerio/wasmer"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
byteorder = "1.3.2" byteorder = "1.3"
lazy_static = "1.3.0" lazy_static = "1.4"
libc = "0.2.60" libc = "0.2.60"
time = "0.1.42" time = "0.1"
wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0" } wasmer-runtime-core = { path = "../runtime-core", version = "0.9.0" }
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
rand = "0.7.0" getrandom = "0.1"
[features] [features]
debug = ["wasmer-runtime-core/debug"] debug = ["wasmer-runtime-core/debug"]

View File

@ -12,14 +12,14 @@ pub use self::windows::*;
use libc::c_char; use libc::c_char;
use crate::{allocate_on_stack, EmscriptenData}; use crate::{
allocate_on_stack,
ptr::{Array, WasmPtr},
EmscriptenData,
};
use std::os::raw::c_int; use std::os::raw::c_int;
use wasmer_runtime_core::{ use wasmer_runtime_core::{types::ValueType, vm::Ctx};
memory::ptr::{Array, WasmPtr},
types::ValueType,
vm::Ctx,
};
pub fn call_malloc(ctx: &mut Ctx, size: u32) -> u32 { pub fn call_malloc(ctx: &mut Ctx, size: u32) -> u32 {
get_emscripten_data(ctx) get_emscripten_data(ctx)

View File

@ -9,11 +9,9 @@ use std::mem;
use std::os::raw::c_char; use std::os::raw::c_char;
use crate::env::{call_malloc, call_malloc_with_cast, EmAddrInfo, EmSockAddr}; use crate::env::{call_malloc, call_malloc_with_cast, EmAddrInfo, EmSockAddr};
use crate::ptr::{Array, WasmPtr};
use crate::utils::{copy_cstr_into_wasm, copy_terminated_array_of_cstrs}; use crate::utils::{copy_cstr_into_wasm, copy_terminated_array_of_cstrs};
use wasmer_runtime_core::{ use wasmer_runtime_core::vm::Ctx;
memory::ptr::{Array, WasmPtr},
vm::Ctx,
};
// #[no_mangle] // #[no_mangle]
/// emscripten: _getenv // (name: *const char) -> *const c_char; /// emscripten: _getenv // (name: *const char) -> *const c_char;

View File

@ -6,8 +6,9 @@ use std::mem;
use std::os::raw::c_char; use std::os::raw::c_char;
use crate::env::{call_malloc, EmAddrInfo}; use crate::env::{call_malloc, EmAddrInfo};
use crate::ptr::WasmPtr;
use crate::utils::{copy_cstr_into_wasm, read_string_from_wasm}; use crate::utils::{copy_cstr_into_wasm, read_string_from_wasm};
use wasmer_runtime_core::{memory::ptr::WasmPtr, vm::Ctx}; use wasmer_runtime_core::vm::Ctx;
extern "C" { extern "C" {
#[link_name = "_putenv"] #[link_name = "_putenv"]

View File

@ -10,22 +10,22 @@ pub fn ___cxa_allocate_exception(ctx: &mut Ctx, size: u32) -> u32 {
pub fn ___cxa_current_primary_exception(_ctx: &mut Ctx) -> u32 { pub fn ___cxa_current_primary_exception(_ctx: &mut Ctx) -> u32 {
debug!("emscripten::___cxa_current_primary_exception"); debug!("emscripten::___cxa_current_primary_exception");
unimplemented!() unimplemented!("emscripten::___cxa_current_primary_exception")
} }
pub fn ___cxa_decrement_exception_refcount(_ctx: &mut Ctx, _a: u32) { pub fn ___cxa_decrement_exception_refcount(_ctx: &mut Ctx, _a: u32) {
debug!("emscripten::___cxa_decrement_exception_refcount({})", _a); debug!("emscripten::___cxa_decrement_exception_refcount({})", _a);
unimplemented!() unimplemented!("emscripten::___cxa_decrement_exception_refcount({})", _a)
} }
pub fn ___cxa_increment_exception_refcount(_ctx: &mut Ctx, _a: u32) { pub fn ___cxa_increment_exception_refcount(_ctx: &mut Ctx, _a: u32) {
debug!("emscripten::___cxa_increment_exception_refcount({})", _a); debug!("emscripten::___cxa_increment_exception_refcount({})", _a);
unimplemented!() unimplemented!("emscripten::___cxa_increment_exception_refcount({})", _a)
} }
pub fn ___cxa_rethrow_primary_exception(_ctx: &mut Ctx, _a: u32) { pub fn ___cxa_rethrow_primary_exception(_ctx: &mut Ctx, _a: u32) {
debug!("emscripten::___cxa_rethrow_primary_exception({})", _a); debug!("emscripten::___cxa_rethrow_primary_exception({})", _a);
unimplemented!() unimplemented!("emscripten::___cxa_rethrow_primary_exception({})", _a)
} }
/// emscripten: ___cxa_throw /// emscripten: ___cxa_throw

View File

@ -15,13 +15,13 @@ use wasmer_runtime_core::vm::Ctx;
/// getprotobyname /// getprotobyname
pub fn getprotobyname(_ctx: &mut Ctx, _name_ptr: i32) -> i32 { pub fn getprotobyname(_ctx: &mut Ctx, _name_ptr: i32) -> i32 {
debug!("emscripten::getprotobyname"); debug!("emscripten::getprotobyname");
unimplemented!() unimplemented!("emscripten::getprotobyname")
} }
/// getprotobynumber /// getprotobynumber
pub fn getprotobynumber(_ctx: &mut Ctx, _one: i32) -> i32 { pub fn getprotobynumber(_ctx: &mut Ctx, _one: i32) -> i32 {
debug!("emscripten::getprotobynumber"); debug!("emscripten::getprotobynumber");
unimplemented!() unimplemented!("emscripten::getprotobynumber")
} }
/// sigdelset /// sigdelset
@ -53,11 +53,11 @@ pub fn sigfillset(ctx: &mut Ctx, set: i32) -> i32 {
/// tzset /// tzset
pub fn tzset(_ctx: &mut Ctx) { pub fn tzset(_ctx: &mut Ctx) {
debug!("emscripten::tzset - stub"); debug!("emscripten::tzset - stub");
//unimplemented!() //unimplemented!("emscripten::tzset - stub")
} }
/// strptime /// strptime
pub fn strptime(_ctx: &mut Ctx, _one: i32, _two: i32, _three: i32) -> i32 { pub fn strptime(_ctx: &mut Ctx, _one: i32, _two: i32, _three: i32) -> i32 {
debug!("emscripten::strptime"); debug!("emscripten::strptime");
unimplemented!() unimplemented!("emscripten::strptime")
} }

View File

@ -36,11 +36,11 @@ pub fn printf(_ctx: &mut Ctx, memory_offset: i32, extra: i32) -> i32 {
/// chroot /// chroot
pub fn chroot(_ctx: &mut Ctx, _name_ptr: i32) -> i32 { pub fn chroot(_ctx: &mut Ctx, _name_ptr: i32) -> i32 {
debug!("emscripten::chroot"); debug!("emscripten::chroot");
unimplemented!() unimplemented!("emscripten::chroot")
} }
/// getpwuid /// getpwuid
pub fn getpwuid(_ctx: &mut Ctx, _uid: i32) -> i32 { pub fn getpwuid(_ctx: &mut Ctx, _uid: i32) -> i32 {
debug!("emscripten::getpwuid"); debug!("emscripten::getpwuid");
unimplemented!() unimplemented!("emscripten::getpwuid")
} }

View File

@ -62,6 +62,7 @@ mod math;
mod memory; mod memory;
mod process; mod process;
mod pthread; mod pthread;
mod ptr;
mod signal; mod signal;
mod storage; mod storage;
mod syscalls; mod syscalls;
@ -473,11 +474,7 @@ impl EmscriptenGlobals {
let (memory_min, memory_max, shared) = get_emscripten_memory_size(&module)?; let (memory_min, memory_max, shared) = get_emscripten_memory_size(&module)?;
// Memory initialization // Memory initialization
let memory_type = MemoryDescriptor { let memory_type = MemoryDescriptor::new(memory_min, memory_max, shared)?;
minimum: memory_min,
maximum: memory_max,
shared: shared,
};
let memory = Memory::new(memory_type).unwrap(); let memory = Memory::new(memory_type).unwrap();
let table_type = TableDescriptor { let table_type = TableDescriptor {
@ -734,8 +731,9 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
"___syscall345" => func!(crate::syscalls::___syscall345), "___syscall345" => func!(crate::syscalls::___syscall345),
// Process // Process
"abort" => func!(crate::process::em_abort), "abort" => func!(crate::process::_abort),
"_abort" => func!(crate::process::_abort), "_abort" => func!(crate::process::_abort),
"_prctl" => func!(crate::process::_prctl),
"abortStackOverflow" => func!(crate::process::abort_stack_overflow), "abortStackOverflow" => func!(crate::process::abort_stack_overflow),
"_llvm_trap" => func!(crate::process::_llvm_trap), "_llvm_trap" => func!(crate::process::_llvm_trap),
"_fork" => func!(crate::process::_fork), "_fork" => func!(crate::process::_fork),
@ -828,6 +826,9 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
"_gmtime" => func!(crate::time::_gmtime), "_gmtime" => func!(crate::time::_gmtime),
// Math // Math
"sqrt" => func!(crate::math::sqrt),
"floor" => func!(crate::math::floor),
"fabs" => func!(crate::math::fabs),
"f64-rem" => func!(crate::math::f64_rem), "f64-rem" => func!(crate::math::f64_rem),
"_llvm_copysign_f32" => func!(crate::math::_llvm_copysign_f32), "_llvm_copysign_f32" => func!(crate::math::_llvm_copysign_f32),
"_llvm_copysign_f64" => func!(crate::math::_llvm_copysign_f64), "_llvm_copysign_f64" => func!(crate::math::_llvm_copysign_f64),

View File

@ -88,6 +88,21 @@ pub fn log(_ctx: &mut Ctx, value: f64) -> f64 {
value.ln() value.ln()
} }
// emscripten: global.Math sqrt
pub fn sqrt(_ctx: &mut Ctx, value: f64) -> f64 {
value.sqrt()
}
// emscripten: global.Math floor
pub fn floor(_ctx: &mut Ctx, value: f64) -> f64 {
value.floor()
}
// emscripten: global.Math fabs
pub fn fabs(_ctx: &mut Ctx, value: f64) -> f64 {
value.abs()
}
// emscripten: asm2wasm.f64-to-int // emscripten: asm2wasm.f64-to-int
pub fn f64_to_int(_ctx: &mut Ctx, value: f64) -> i32 { pub fn f64_to_int(_ctx: &mut Ctx, value: f64) -> i32 {
debug!("emscripten::f64_to_int {}", value); debug!("emscripten::f64_to_int {}", value);

View File

@ -1,11 +1,10 @@
use libc::{abort, c_char, c_int, exit, EAGAIN}; use libc::{abort, c_int, exit, EAGAIN};
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
type PidT = libc::pid_t; type PidT = libc::pid_t;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
type PidT = c_int; type PidT = c_int;
use std::ffi::CStr;
use wasmer_runtime_core::vm::Ctx; use wasmer_runtime_core::vm::Ctx;
pub fn abort_with_message(ctx: &mut Ctx, message: &str) { pub fn abort_with_message(ctx: &mut Ctx, message: &str) {
@ -21,6 +20,12 @@ pub fn _abort(_ctx: &mut Ctx) {
} }
} }
pub fn _prctl(ctx: &mut Ctx, _a: i32, _b: i32) -> i32 {
debug!("emscripten::_prctl");
abort_with_message(ctx, "missing function: prctl");
-1
}
pub fn _fork(_ctx: &mut Ctx) -> PidT { pub fn _fork(_ctx: &mut Ctx) -> PidT {
debug!("emscripten::_fork"); debug!("emscripten::_fork");
// unsafe { // unsafe {
@ -45,18 +50,6 @@ pub fn _exit(_ctx: &mut Ctx, status: c_int) {
unsafe { exit(status) } unsafe { exit(status) }
} }
pub fn em_abort(ctx: &mut Ctx, message: u32) {
debug!("emscripten::em_abort {}", message);
let message_addr = emscripten_memory_pointer!(ctx.memory(0), message) as *mut c_char;
unsafe {
let message = CStr::from_ptr(message_addr)
.to_str()
.unwrap_or("Unexpected abort");
abort_with_message(ctx, message);
}
}
pub fn _kill(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { pub fn _kill(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
debug!("emscripten::_kill"); debug!("emscripten::_kill");
-1 -1

116
lib/emscripten/src/ptr.rs Normal file
View File

@ -0,0 +1,116 @@
//! This is a wrapper around the `WasmPtr` abstraction that does not allow deref of address 0
//! This is a common assumption in Emscripten code
// this is a wrapper with extra logic around the runtime-core `WasmPtr`, so we
// don't want to warn about unusued code here
#![allow(dead_code)]
use std::{cell::Cell, fmt};
pub use wasmer_runtime_core::memory::ptr::Array;
use wasmer_runtime_core::{
memory::{ptr, Memory},
types::{ValueType, WasmExternType},
};
#[repr(transparent)]
pub struct WasmPtr<T: Copy, Ty = ptr::Item>(ptr::WasmPtr<T, Ty>);
unsafe impl<T: Copy, Ty> ValueType for WasmPtr<T, Ty> {}
impl<T: Copy, Ty> Copy for WasmPtr<T, Ty> {}
impl<T: Copy, Ty> Clone for WasmPtr<T, Ty> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T: Copy, Ty> fmt::Debug for WasmPtr<T, Ty> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.0)
}
}
unsafe impl<T: Copy, Ty> WasmExternType for WasmPtr<T, Ty> {
type Native = <ptr::WasmPtr<T, Ty> as WasmExternType>::Native;
fn to_native(self) -> Self::Native {
self.0.to_native()
}
fn from_native(n: Self::Native) -> Self {
Self(ptr::WasmPtr::from_native(n))
}
}
impl<T: Copy, Ty> PartialEq for WasmPtr<T, Ty> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T: Copy, Ty> Eq for WasmPtr<T, Ty> {}
impl<T: Copy, Ty> WasmPtr<T, Ty> {
#[inline(always)]
pub fn new(offset: u32) -> Self {
Self(ptr::WasmPtr::new(offset))
}
#[inline(always)]
pub fn offset(self) -> u32 {
self.0.offset()
}
}
impl<T: Copy + ValueType> WasmPtr<T, ptr::Item> {
#[inline(always)]
pub fn deref<'a>(self, memory: &'a Memory) -> Option<&'a Cell<T>> {
if self.0.offset() == 0 {
None
} else {
self.0.deref(memory)
}
}
#[inline(always)]
pub unsafe fn deref_mut<'a>(self, memory: &'a Memory) -> Option<&'a mut Cell<T>> {
if self.0.offset() == 0 {
None
} else {
self.0.deref_mut(memory)
}
}
}
impl<T: Copy + ValueType> WasmPtr<T, ptr::Array> {
#[inline(always)]
pub fn deref<'a>(self, memory: &'a Memory, index: u32, length: u32) -> Option<&'a [Cell<T>]> {
if self.0.offset() == 0 {
None
} else {
self.0.deref(memory, index, length)
}
}
#[inline]
pub unsafe fn deref_mut<'a>(
self,
memory: &'a Memory,
index: u32,
length: u32,
) -> Option<&'a mut [Cell<T>]> {
if self.0.offset() == 0 {
None
} else {
self.0.deref_mut(memory, index, length)
}
}
#[inline(always)]
pub fn get_utf8_string<'a>(self, memory: &'a Memory, str_len: u32) -> Option<&'a str> {
if self.0.offset() == 0 {
None
} else {
self.0.get_utf8_string(memory, str_len)
}
}
}

View File

@ -10,7 +10,10 @@ pub use self::unix::*;
#[cfg(windows)] #[cfg(windows)]
pub use self::windows::*; pub use self::windows::*;
use crate::utils::{copy_stat_into_wasm, get_cstr_path, get_current_directory}; use crate::{
ptr::{Array, WasmPtr},
utils::{copy_stat_into_wasm, get_cstr_path, get_current_directory},
};
use super::varargs::VarArgs; use super::varargs::VarArgs;
use byteorder::{ByteOrder, LittleEndian}; use byteorder::{ByteOrder, LittleEndian};
@ -40,10 +43,7 @@ use libc::{
write, write,
// ENOTTY, // ENOTTY,
}; };
use wasmer_runtime_core::{ use wasmer_runtime_core::vm::Ctx;
memory::ptr::{Array, WasmPtr},
vm::Ctx,
};
use super::env; use super::env;
use std::cell::Cell; use std::cell::Cell;

View File

@ -1,4 +1,4 @@
use crate::varargs::VarArgs; use crate::{ptr::WasmPtr, varargs::VarArgs};
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
use libc::size_t; use libc::size_t;
/// NOTE: TODO: These syscalls only support wasm_32 for now because they assume offsets are u32 /// NOTE: TODO: These syscalls only support wasm_32 for now because they assume offsets are u32
@ -111,7 +111,7 @@ fn translate_ioctl(wasm_ioctl: u32) -> c_ulong {
#[allow(unused_imports)] #[allow(unused_imports)]
use std::ffi::CStr; use std::ffi::CStr;
use wasmer_runtime_core::{memory::ptr::WasmPtr, vm::Ctx}; use wasmer_runtime_core::vm::Ctx;
use crate::env::EmSockAddr; use crate::env::EmSockAddr;
use crate::utils::{self, get_cstr_path}; use crate::utils::{self, get_cstr_path};
@ -259,7 +259,7 @@ pub fn ___syscall194(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
ftruncate64(_fd, _length) ftruncate64(_fd, _length)
} }
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
unimplemented!() unimplemented!("emscripten::___syscall194 (ftruncate64) {}", _which)
} }
/// lchown /// lchown
@ -1111,6 +1111,6 @@ pub fn ___syscall324(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
} }
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
{ {
unimplemented!() unimplemented!("emscripten::___syscall324 (fallocate) {}", _which)
} }
} }

View File

@ -2,7 +2,6 @@ use crate::utils::{copy_cstr_into_wasm, get_cstr_path};
use crate::varargs::VarArgs; use crate::varargs::VarArgs;
use libc::mkdir; use libc::mkdir;
use libc::open; use libc::open;
use rand::Rng;
use std::env; use std::env;
use std::ffi::CString; use std::ffi::CString;
use std::fs::File; use std::fs::File;
@ -39,7 +38,8 @@ pub fn ___syscall5(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int {
let ptr = tmp_dir_c_str.as_ptr() as *const i8; let ptr = tmp_dir_c_str.as_ptr() as *const i8;
let mut urandom_file = File::create(tmp_dir).unwrap(); let mut urandom_file = File::create(tmp_dir).unwrap();
// create some random bytes and put them into the file // create some random bytes and put them into the file
let random_bytes = rand::thread_rng().gen::<[u8; 32]>(); let mut random_bytes = [0u8; 32];
getrandom::getrandom(&mut random_bytes).unwrap();
let _ = urandom_file.write_all(&random_bytes).unwrap(); let _ = urandom_file.write_all(&random_bytes).unwrap();
// put the file path string into wasm memory // put the file path string into wasm memory
let urandom_file_offset = unsafe { copy_cstr_into_wasm(ctx, ptr) }; let urandom_file_offset = unsafe { copy_cstr_into_wasm(ctx, ptr) };
@ -66,13 +66,13 @@ pub fn ___syscall5(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int {
/// link /// link
pub fn ___syscall9(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_int { pub fn ___syscall9(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall9 (link) {}", _which); debug!("emscripten::___syscall9 (link) {}", _which);
unimplemented!() unimplemented!("emscripten::___syscall9 (link) {}", _which);
} }
/// ftruncate64 /// ftruncate64
pub fn ___syscall194(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { pub fn ___syscall194(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
debug!("emscripten::___syscall194 - stub"); debug!("emscripten::___syscall194 - stub");
unimplemented!() unimplemented!("emscripten::___syscall194 - stub")
} }
// chown // chown
@ -86,13 +86,13 @@ pub fn ___syscall212(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_i
/// access /// access
pub fn ___syscall33(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_int { pub fn ___syscall33(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall33 (access) {}", _which); debug!("emscripten::___syscall33 (access) {}", _which);
unimplemented!() unimplemented!("emscripten::___syscall33 (access) {}", _which);
} }
/// nice /// nice
pub fn ___syscall34(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_int { pub fn ___syscall34(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall34 (nice) {}", _which); debug!("emscripten::___syscall34 (nice) {}", _which);
unimplemented!() unimplemented!("emscripten::___syscall34 (nice) {}", _which);
} }
// mkdir // mkdir
@ -113,19 +113,19 @@ pub fn ___syscall39(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int
/// dup /// dup
pub fn ___syscall41(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { pub fn ___syscall41(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall41 (dup) {}", _which); debug!("emscripten::___syscall41 (dup) {}", _which);
unimplemented!() unimplemented!("emscripten::___syscall41 (dup) {}", _which);
} }
/// getrusage /// getrusage
pub fn ___syscall77(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { pub fn ___syscall77(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall77 (getrusage) {}", _which); debug!("emscripten::___syscall77 (getrusage) {}", _which);
unimplemented!() unimplemented!("emscripten::___syscall77 (getrusage) {}", _which);
} }
/// symlink /// symlink
pub fn ___syscall83(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { pub fn ___syscall83(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall83 (symlink) {}", _which); debug!("emscripten::___syscall83 (symlink) {}", _which);
unimplemented!() unimplemented!("emscripten::___syscall83 (symlink) {}", _which);
} }
/// readlink /// readlink
@ -143,38 +143,38 @@ pub fn ___syscall132(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_
/// lchown /// lchown
pub fn ___syscall198(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { pub fn ___syscall198(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall198 (lchown) {}", _which); debug!("emscripten::___syscall198 (lchown) {}", _which);
unimplemented!() unimplemented!("emscripten::___syscall198 (lchown) {}", _which);
} }
/// getgid32 /// getgid32
pub fn ___syscall200(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { pub fn ___syscall200(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
debug!("emscripten::___syscall200 (getgid32)"); debug!("emscripten::___syscall200 (getgid32)");
unimplemented!(); unimplemented!("emscripten::___syscall200 (getgid32)");
} }
// geteuid32 // geteuid32
pub fn ___syscall201(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { pub fn ___syscall201(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
debug!("emscripten::___syscall201 (geteuid32)"); debug!("emscripten::___syscall201 (geteuid32)");
unimplemented!(); unimplemented!("emscripten::___syscall201 (geteuid32)");
} }
// getegid32 // getegid32
pub fn ___syscall202(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { pub fn ___syscall202(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
// gid_t // gid_t
debug!("emscripten::___syscall202 (getegid32)"); debug!("emscripten::___syscall202 (getegid32)");
unimplemented!(); unimplemented!("emscripten::___syscall202 (getegid32)");
} }
/// getgroups /// getgroups
pub fn ___syscall205(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { pub fn ___syscall205(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall205 (getgroups) {}", _which); debug!("emscripten::___syscall205 (getgroups) {}", _which);
unimplemented!() unimplemented!("emscripten::___syscall205 (getgroups) {}", _which);
} }
/// madvise /// madvise
pub fn ___syscall219(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { pub fn ___syscall219(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall212 (chown) {}", _which); debug!("emscripten::___syscall212 (chown) {}", _which);
unimplemented!() unimplemented!("emscripten::___syscall212 (chown) {}", _which);
} }
/// dup3 /// dup3
@ -194,7 +194,7 @@ pub fn ___syscall54(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_in
/// fchmod /// fchmod
pub fn ___syscall94(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { pub fn ___syscall94(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall118 (fchmod) {}", _which); debug!("emscripten::___syscall118 (fchmod) {}", _which);
unimplemented!() unimplemented!("emscripten::___syscall118 (fchmod) {}", _which);
} }
// socketcall // socketcall
@ -209,7 +209,7 @@ pub fn ___syscall102(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_i
/// fsync /// fsync
pub fn ___syscall118(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { pub fn ___syscall118(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall118 (fsync) {}", _which); debug!("emscripten::___syscall118 (fsync) {}", _which);
unimplemented!() unimplemented!("emscripten::___syscall118 (fsync) {}", _which);
} }
// pread // pread
@ -247,7 +247,7 @@ pub fn ___syscall142(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_i
/// fdatasync /// fdatasync
pub fn ___syscall148(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { pub fn ___syscall148(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall148 (fdatasync) {}", _which); debug!("emscripten::___syscall148 (fdatasync) {}", _which);
unimplemented!(); unimplemented!("emscripten::___syscall148 (fdatasync) {}", _which);
} }
// setpgid // setpgid
@ -300,11 +300,11 @@ pub fn ___syscall221(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_
/// fchown /// fchown
pub fn ___syscall207(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { pub fn ___syscall207(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall207 (fchown) {}", _which); debug!("emscripten::___syscall207 (fchown) {}", _which);
unimplemented!() unimplemented!("emscripten::___syscall207 (fchown) {}", _which)
} }
/// fallocate /// fallocate
pub fn ___syscall324(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { pub fn ___syscall324(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall324 (fallocate) {}", _which); debug!("emscripten::___syscall324 (fallocate) {}", _which);
unimplemented!() unimplemented!("emscripten::___syscall324 (fallocate) {}", _which)
} }

View File

@ -24,7 +24,9 @@ pub fn is_emscripten_module(module: &Module) -> bool {
.namespace_table .namespace_table
.get(import_name.namespace_index); .get(import_name.namespace_index);
let field = module.info().name_table.get(import_name.name_index); let field = module.info().name_table.get(import_name.name_index);
if (field == "_emscripten_memcpy_big" || field == "emscripten_memcpy_big") if (field == "_emscripten_memcpy_big"
|| field == "emscripten_memcpy_big"
|| field == "__map_file")
&& namespace == "env" && namespace == "env"
{ {
return true; return true;

View File

@ -1,17 +1,16 @@
[package] [package]
name = "wasmer-llvm-backend" name = "wasmer-llvm-backend"
version = "0.6.0" version = "0.9.0"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"] authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
edition = "2018" edition = "2018"
readme = "README.md" readme = "README.md"
[dependencies] [dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0" } wasmer-runtime-core = { path = "../runtime-core", version = "0.9.0" }
wasmparser = "0.35.1" wasmparser = "0.39.1"
smallvec = "0.6.10" smallvec = "0.6"
goblin = "0.0.24" goblin = "0.0.24"
libc = "0.2.60" libc = "0.2.60"
capstone = { version = "0.6.0", optional = true }
byteorder = "1" byteorder = "1"
[dependencies.inkwell] [dependencies.inkwell]
@ -21,21 +20,20 @@ default-features = false
features = ["llvm8-0", "target-x86"] features = ["llvm8-0", "target-x86"]
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
nix = "0.15.0" nix = "0.15"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.8", features = ["memoryapi"] } winapi = { version = "0.3", features = ["memoryapi"] }
[build-dependencies] [build-dependencies]
cc = "1.0" cc = "1.0"
lazy_static = "1.3.0" lazy_static = "1.4"
regex = "1.2.1" regex = "1.2"
semver = "0.9" semver = "0.9"
rustc_version = "0.2.3" rustc_version = "0.2"
[dev-dependencies] [dev-dependencies]
wabt = "0.9.1" wabt = "0.9.1"
[features] [features]
debug = ["wasmer-runtime-core/debug"] debug = ["wasmer-runtime-core/debug"]
disasm = ["capstone"]

View File

@ -1,21 +1,21 @@
<p align="center"> <p align="center">
<a href="https://wasmer.io" target="_blank" rel="noopener noreferrer"> <a href="https://wasmer.io" target="_blank" rel="noopener noreferrer">
<img width="400" src="https://raw.githubusercontent.com/wasmerio/wasmer/master/logo.png" alt="Wasmer logo"> <img width="300" src="https://raw.githubusercontent.com/wasmerio/wasmer/master/logo.png" alt="Wasmer logo">
</a> </a>
</p> </p>
<p align="center"> <p align="center">
<a href="https://circleci.com/gh/wasmerio/wasmer/"> <a href="https://dev.azure.com/wasmerio/wasmer/_build/latest?definitionId=3&branchName=master">
<img src="https://img.shields.io/circleci/project/github/wasmerio/wasmer/master.svg" alt="Build Status"> <img src="https://img.shields.io/azure-devops/build/wasmerio/wasmer/3.svg?style=flat-square" alt="Build Status">
</a> </a>
<a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE"> <a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/wasmerio/wasmer.svg" alt="License"> <img src="https://img.shields.io/github/license/wasmerio/wasmer.svg?style=flat-square" alt="License">
</a> </a>
<a href="https://spectrum.chat/wasmer"> <a href="https://spectrum.chat/wasmer">
<img src="https://withspectrum.github.io/badge/badge.svg" alt="Join the Wasmer Community"> <img src="https://withspectrum.github.io/badge/badge.svg" alt="Join the Wasmer Community">
</a> </a>
<a href="https://crates.io/crates/wasmer-llvm-backend"> <a href="https://crates.io/crates/wasmer-llvm-backend">
<img src="https://img.shields.io/crates/d/wasmer-llvm-backend.svg" alt="Number of downloads from crates.io"> <img src="https://img.shields.io/crates/d/wasmer-llvm-backend.svg?style=flat-square" alt="Number of downloads from crates.io">
</a> </a>
<a href="https://docs.rs/wasmer-llvm-backend"> <a href="https://docs.rs/wasmer-llvm-backend">
<img src="https://docs.rs/wasmer-llvm-backend/badge.svg" alt="Read our API documentation"> <img src="https://docs.rs/wasmer-llvm-backend/badge.svg" alt="Read our API documentation">
@ -36,7 +36,7 @@ This crate represents the LLVM backend integration for Wasmer.
If you are using the `wasmer` CLI, you can specify the backend with: If you are using the `wasmer` CLI, you can specify the backend with:
```bash ```sh
wasmer run program.wasm --backend=llvm wasmer run program.wasm --backend=llvm
``` ```

View File

@ -1,21 +1,24 @@
use super::stackmap::StackmapRegistry; use super::stackmap::StackmapRegistry;
use crate::intrinsics::Intrinsics; use crate::{
use crate::structs::{Callbacks, LLVMModule, LLVMResult, MemProtect}; intrinsics::Intrinsics,
structs::{Callbacks, LLVMModule, LLVMResult, MemProtect},
};
use inkwell::{ use inkwell::{
memory_buffer::MemoryBuffer, memory_buffer::MemoryBuffer,
module::Module, module::Module,
targets::{CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine}, targets::{FileType, TargetMachine},
OptimizationLevel,
}; };
use libc::c_char; use libc::c_char;
use std::{ use std::{
any::Any, any::Any,
cell::RefCell,
ffi::{c_void, CString}, ffi::{c_void, CString},
fs::File, fs::File,
io::Write, io::Write,
mem, mem,
ops::Deref, ops::Deref,
ptr::{self, NonNull}, ptr::{self, NonNull},
rc::Rc,
slice, str, slice, str,
sync::{Arc, Once}, sync::{Arc, Once},
}; };
@ -28,7 +31,7 @@ use wasmer_runtime_core::{
module::ModuleInfo, module::ModuleInfo,
state::ModuleStateMap, state::ModuleStateMap,
structures::TypedIndex, structures::TypedIndex,
typed_func::{Wasm, WasmTrapInfo}, typed_func::{Trampoline, Wasm, WasmTrapInfo},
types::{LocalFuncIndex, SigIndex}, types::{LocalFuncIndex, SigIndex},
vm, vmcalls, vm, vmcalls,
}; };
@ -58,7 +61,7 @@ extern "C" {
#[allow(improper_ctypes)] #[allow(improper_ctypes)]
fn invoke_trampoline( fn invoke_trampoline(
trampoline: unsafe extern "C" fn(*mut vm::Ctx, NonNull<vm::Func>, *const u64, *mut u64), trampoline: Trampoline,
vmctx_ptr: *mut vm::Ctx, vmctx_ptr: *mut vm::Ctx,
func_ptr: NonNull<vm::Func>, func_ptr: NonNull<vm::Func>,
params: *const u64, params: *const u64,
@ -168,34 +171,14 @@ pub struct LLVMBackend {
impl LLVMBackend { impl LLVMBackend {
pub fn new( pub fn new(
module: Module, module: Rc<RefCell<Module>>,
_intrinsics: Intrinsics, _intrinsics: Intrinsics,
_stackmaps: &StackmapRegistry, _stackmaps: &StackmapRegistry,
_module_info: &ModuleInfo, _module_info: &ModuleInfo,
target_machine: &TargetMachine,
) -> (Self, LLVMCache) { ) -> (Self, LLVMCache) {
Target::initialize_x86(&InitializationConfig {
asm_parser: true,
asm_printer: true,
base: true,
disassembler: true,
info: true,
machine_code: true,
});
let triple = TargetMachine::get_default_triple().to_string();
let target = Target::from_triple(&triple).unwrap();
let target_machine = target
.create_target_machine(
&triple,
&TargetMachine::get_host_cpu_name().to_string(),
&TargetMachine::get_host_cpu_features().to_string(),
OptimizationLevel::Aggressive,
RelocMode::PIC,
CodeModel::Default,
)
.unwrap();
let memory_buffer = target_machine let memory_buffer = target_machine
.write_to_memory_buffer(&module, FileType::Object) .write_to_memory_buffer(&module.borrow_mut(), FileType::Object)
.unwrap(); .unwrap();
let mem_buf_slice = memory_buffer.as_slice(); let mem_buf_slice = memory_buffer.as_slice();
@ -408,12 +391,7 @@ impl RunnableModule for LLVMBackend {
} }
fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option<Wasm> { fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option<Wasm> {
let trampoline: unsafe extern "C" fn( let trampoline: Trampoline = unsafe {
*mut vm::Ctx,
NonNull<vm::Func>,
*const u64,
*mut u64,
) = unsafe {
let name = if cfg!(target_os = "macos") { let name = if cfg!(target_os = "macos") {
format!("_trmp{}", sig_index.index()) format!("_trmp{}", sig_index.index())
} else { } else {
@ -477,33 +455,3 @@ impl CacheGen for LLVMCache {
Ok(([].as_ref().into(), memory)) Ok(([].as_ref().into(), memory))
} }
} }
#[cfg(feature = "disasm")]
unsafe fn disass_ptr(ptr: *const u8, size: usize, inst_count: usize) {
use capstone::arch::BuildsCapstone;
let mut cs = capstone::Capstone::new() // Call builder-pattern
.x86() // X86 architecture
.mode(capstone::arch::x86::ArchMode::Mode64) // 64-bit mode
.detail(true) // Generate extra instruction details
.build()
.expect("Failed to create Capstone object");
// Get disassembled instructions
let insns = cs
.disasm_count(
std::slice::from_raw_parts(ptr, size),
ptr as u64,
inst_count,
)
.expect("Failed to disassemble");
println!("count = {}", insns.len());
for insn in insns.iter() {
println!(
"0x{:x}: {:6} {}",
insn.address(),
insn.mnemonic().unwrap_or(""),
insn.op_str().unwrap_or("")
);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -6,11 +6,16 @@ use inkwell::{
types::{ types::{
BasicType, FloatType, FunctionType, IntType, PointerType, StructType, VectorType, VoidType, BasicType, FloatType, FunctionType, IntType, PointerType, StructType, VectorType, VoidType,
}, },
values::{BasicValue, BasicValueEnum, FloatValue, FunctionValue, IntValue, PointerValue}, values::{
BasicValue, BasicValueEnum, FloatValue, FunctionValue, InstructionValue, IntValue,
PointerValue, VectorValue,
},
AddressSpace, AddressSpace,
}; };
use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::rc::Rc;
use wasmer_runtime_core::{ use wasmer_runtime_core::{
memory::MemoryType, memory::MemoryType,
module::ModuleInfo, module::ModuleInfo,
@ -19,6 +24,7 @@ use wasmer_runtime_core::{
GlobalIndex, ImportedFuncIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex, GlobalIndex, ImportedFuncIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex,
TableIndex, Type, TableIndex, Type,
}, },
units::Pages,
vm::{Ctx, INTERNALS_SIZE}, vm::{Ctx, INTERNALS_SIZE},
}; };
@ -47,16 +53,6 @@ pub struct Intrinsics {
pub sqrt_f32x4: FunctionValue, pub sqrt_f32x4: FunctionValue,
pub sqrt_f64x2: FunctionValue, pub sqrt_f64x2: FunctionValue,
pub minimum_f32: FunctionValue,
pub minimum_f64: FunctionValue,
pub minimum_f32x4: FunctionValue,
pub minimum_f64x2: FunctionValue,
pub maximum_f32: FunctionValue,
pub maximum_f64: FunctionValue,
pub maximum_f32x4: FunctionValue,
pub maximum_f64x2: FunctionValue,
pub ceil_f32: FunctionValue, pub ceil_f32: FunctionValue,
pub ceil_f64: FunctionValue, pub ceil_f64: FunctionValue,
@ -125,6 +121,8 @@ pub struct Intrinsics {
pub i128_zero: IntValue, pub i128_zero: IntValue,
pub f32_zero: FloatValue, pub f32_zero: FloatValue,
pub f64_zero: FloatValue, pub f64_zero: FloatValue,
pub f32x4_zero: VectorValue,
pub f64x2_zero: VectorValue,
pub trap_unreachable: BasicValueEnum, pub trap_unreachable: BasicValueEnum,
pub trap_call_indirect_sig: BasicValueEnum, pub trap_call_indirect_sig: BasicValueEnum,
@ -191,6 +189,8 @@ impl Intrinsics {
let i128_zero = i128_ty.const_int(0, false); let i128_zero = i128_ty.const_int(0, false);
let f32_zero = f32_ty.const_float(0.0); let f32_zero = f32_ty.const_float(0.0);
let f64_zero = f64_ty.const_float(0.0); let f64_zero = f64_ty.const_float(0.0);
let f32x4_zero = f32x4_ty.const_zero();
let f64x2_zero = f64x2_ty.const_zero();
let i1_ty_basic = i1_ty.as_basic_type_enum(); let i1_ty_basic = i1_ty.as_basic_type_enum();
let i32_ty_basic = i32_ty.as_basic_type_enum(); let i32_ty_basic = i32_ty.as_basic_type_enum();
@ -303,8 +303,6 @@ impl Intrinsics {
let ret_f32_take_f32_f32 = f32_ty.fn_type(&[f32_ty_basic, f32_ty_basic], false); let ret_f32_take_f32_f32 = f32_ty.fn_type(&[f32_ty_basic, f32_ty_basic], false);
let ret_f64_take_f64_f64 = f64_ty.fn_type(&[f64_ty_basic, f64_ty_basic], false); let ret_f64_take_f64_f64 = f64_ty.fn_type(&[f64_ty_basic, f64_ty_basic], false);
let ret_f32x4_take_f32x4_f32x4 = f32x4_ty.fn_type(&[f32x4_ty_basic, f32x4_ty_basic], false);
let ret_f64x2_take_f64x2_f64x2 = f64x2_ty.fn_type(&[f64x2_ty_basic, f64x2_ty_basic], false);
let ret_i32_take_ctx_i32_i32 = i32_ty.fn_type( let ret_i32_take_ctx_i32_i32 = i32_ty.fn_type(
&[ctx_ptr_ty.as_basic_type_enum(), i32_ty_basic, i32_ty_basic], &[ctx_ptr_ty.as_basic_type_enum(), i32_ty_basic, i32_ty_basic],
@ -329,32 +327,6 @@ impl Intrinsics {
sqrt_f32x4: module.add_function("llvm.sqrt.v4f32", ret_f32x4_take_f32x4, None), sqrt_f32x4: module.add_function("llvm.sqrt.v4f32", ret_f32x4_take_f32x4, None),
sqrt_f64x2: module.add_function("llvm.sqrt.v2f64", ret_f64x2_take_f64x2, None), sqrt_f64x2: module.add_function("llvm.sqrt.v2f64", ret_f64x2_take_f64x2, None),
minimum_f32: module.add_function("llvm.minnum.f32", ret_f32_take_f32_f32, None),
minimum_f64: module.add_function("llvm.minnum.f64", ret_f64_take_f64_f64, None),
minimum_f32x4: module.add_function(
"llvm.minnum.v4f32",
ret_f32x4_take_f32x4_f32x4,
None,
),
minimum_f64x2: module.add_function(
"llvm.minnum.v2f64",
ret_f64x2_take_f64x2_f64x2,
None,
),
maximum_f32: module.add_function("llvm.maxnum.f32", ret_f32_take_f32_f32, None),
maximum_f64: module.add_function("llvm.maxnum.f64", ret_f64_take_f64_f64, None),
maximum_f32x4: module.add_function(
"llvm.maxnum.v4f32",
ret_f32x4_take_f32x4_f32x4,
None,
),
maximum_f64x2: module.add_function(
"llvm.maxnum.v2f64",
ret_f64x2_take_f64x2_f64x2,
None,
),
ceil_f32: module.add_function("llvm.ceil.f32", ret_f32_take_f32, None), ceil_f32: module.add_function("llvm.ceil.f32", ret_f32_take_f32, None),
ceil_f64: module.add_function("llvm.ceil.f64", ret_f64_take_f64, None), ceil_f64: module.add_function("llvm.ceil.f64", ret_f64_take_f64, None),
@ -455,6 +427,8 @@ impl Intrinsics {
i128_zero, i128_zero,
f32_zero, f32_zero,
f64_zero, f64_zero,
f32x4_zero,
f64x2_zero,
trap_unreachable: i32_zero.as_basic_value_enum(), trap_unreachable: i32_zero.as_basic_value_enum(),
trap_call_indirect_sig: i32_ty.const_int(1, false).as_basic_value_enum(), trap_call_indirect_sig: i32_ty.const_int(1, false).as_basic_value_enum(),
@ -589,11 +563,15 @@ pub enum MemoryCache {
Dynamic { Dynamic {
ptr_to_base_ptr: PointerValue, ptr_to_base_ptr: PointerValue,
ptr_to_bounds: PointerValue, ptr_to_bounds: PointerValue,
minimum: Pages,
maximum: Option<Pages>,
}, },
/// The memory is always in the same place. /// The memory is always in the same place.
Static { Static {
base_ptr: PointerValue, base_ptr: PointerValue,
bounds: IntValue, bounds: IntValue,
minimum: Pages,
maximum: Option<Pages>,
}, },
} }
@ -683,7 +661,12 @@ impl<'a> CtxType<'a> {
ptr ptr
} }
pub fn memory(&mut self, index: MemoryIndex, intrinsics: &Intrinsics) -> MemoryCache { pub fn memory(
&mut self,
index: MemoryIndex,
intrinsics: &Intrinsics,
module: Rc<RefCell<Module>>,
) -> MemoryCache {
let (cached_memories, info, ctx_ptr_value, cache_builder) = ( let (cached_memories, info, ctx_ptr_value, cache_builder) = (
&mut self.cached_memories, &mut self.cached_memories,
self.info, self.info,
@ -692,34 +675,48 @@ impl<'a> CtxType<'a> {
); );
*cached_memories.entry(index).or_insert_with(|| { *cached_memories.entry(index).or_insert_with(|| {
let (memory_array_ptr_ptr, index, memory_type) = match index.local_or_import(info) { let (memory_array_ptr_ptr, index, memory_type, minimum, maximum, field_name) =
LocalOrImport::Local(local_mem_index) => ( match index.local_or_import(info) {
unsafe { LocalOrImport::Local(local_mem_index) => (
cache_builder.build_struct_gep( unsafe {
ctx_ptr_value, cache_builder.build_struct_gep(
offset_to_index(Ctx::offset_memories()), ctx_ptr_value,
"memory_array_ptr_ptr", offset_to_index(Ctx::offset_memories()),
) "memory_array_ptr_ptr",
}, )
local_mem_index.index() as u64, },
info.memories[local_mem_index].memory_type(), local_mem_index.index() as u64,
), info.memories[local_mem_index].memory_type(),
LocalOrImport::Import(import_mem_index) => ( info.memories[local_mem_index].minimum,
unsafe { info.memories[local_mem_index].maximum,
cache_builder.build_struct_gep( "context_field_ptr_to_local_memory",
ctx_ptr_value, ),
offset_to_index(Ctx::offset_imported_memories()), LocalOrImport::Import(import_mem_index) => (
"memory_array_ptr_ptr", unsafe {
) cache_builder.build_struct_gep(
}, ctx_ptr_value,
import_mem_index.index() as u64, offset_to_index(Ctx::offset_imported_memories()),
info.imported_memories[import_mem_index].1.memory_type(), "memory_array_ptr_ptr",
), )
}; },
import_mem_index.index() as u64,
info.imported_memories[import_mem_index].1.memory_type(),
info.imported_memories[import_mem_index].1.minimum,
info.imported_memories[import_mem_index].1.maximum,
"context_field_ptr_to_imported_memory",
),
};
let memory_array_ptr = cache_builder let memory_array_ptr = cache_builder
.build_load(memory_array_ptr_ptr, "memory_array_ptr") .build_load(memory_array_ptr_ptr, "memory_array_ptr")
.into_pointer_value(); .into_pointer_value();
tbaa_label(
module.clone(),
intrinsics,
field_name,
memory_array_ptr.as_instruction_value().unwrap(),
None,
);
let const_index = intrinsics.i32_ty.const_int(index, false); let const_index = intrinsics.i32_ty.const_int(index, false);
let memory_ptr_ptr = unsafe { let memory_ptr_ptr = unsafe {
cache_builder.build_in_bounds_gep( cache_builder.build_in_bounds_gep(
@ -731,6 +728,13 @@ impl<'a> CtxType<'a> {
let memory_ptr = cache_builder let memory_ptr = cache_builder
.build_load(memory_ptr_ptr, "memory_ptr") .build_load(memory_ptr_ptr, "memory_ptr")
.into_pointer_value(); .into_pointer_value();
tbaa_label(
module.clone(),
intrinsics,
"memory_ptr",
memory_ptr.as_instruction_value().unwrap(),
Some(index as u32),
);
let (ptr_to_base_ptr, ptr_to_bounds) = unsafe { let (ptr_to_base_ptr, ptr_to_bounds) = unsafe {
( (
@ -743,15 +747,37 @@ impl<'a> CtxType<'a> {
MemoryType::Dynamic => MemoryCache::Dynamic { MemoryType::Dynamic => MemoryCache::Dynamic {
ptr_to_base_ptr, ptr_to_base_ptr,
ptr_to_bounds, ptr_to_bounds,
minimum,
maximum,
}, },
MemoryType::Static | MemoryType::SharedStatic => MemoryCache::Static { MemoryType::Static | MemoryType::SharedStatic => {
base_ptr: cache_builder let base_ptr = cache_builder
.build_load(ptr_to_base_ptr, "base") .build_load(ptr_to_base_ptr, "base")
.into_pointer_value(), .into_pointer_value();
bounds: cache_builder let bounds = cache_builder
.build_load(ptr_to_bounds, "bounds") .build_load(ptr_to_bounds, "bounds")
.into_int_value(), .into_int_value();
}, tbaa_label(
module.clone(),
intrinsics,
"static_memory_base",
base_ptr.as_instruction_value().unwrap(),
Some(index as u32),
);
tbaa_label(
module.clone(),
intrinsics,
"static_memory_bounds",
bounds.as_instruction_value().unwrap(),
Some(index as u32),
);
MemoryCache::Static {
base_ptr,
bounds,
minimum,
maximum,
}
}
} }
}) })
} }
@ -760,6 +786,7 @@ impl<'a> CtxType<'a> {
&mut self, &mut self,
index: TableIndex, index: TableIndex,
intrinsics: &Intrinsics, intrinsics: &Intrinsics,
module: Rc<RefCell<Module>>,
) -> (PointerValue, PointerValue) { ) -> (PointerValue, PointerValue) {
let (cached_tables, info, ctx_ptr_value, cache_builder) = ( let (cached_tables, info, ctx_ptr_value, cache_builder) = (
&mut self.cached_tables, &mut self.cached_tables,
@ -772,7 +799,7 @@ impl<'a> CtxType<'a> {
ptr_to_base_ptr, ptr_to_base_ptr,
ptr_to_bounds, ptr_to_bounds,
} = *cached_tables.entry(index).or_insert_with(|| { } = *cached_tables.entry(index).or_insert_with(|| {
let (table_array_ptr_ptr, index) = match index.local_or_import(info) { let (table_array_ptr_ptr, index, field_name) = match index.local_or_import(info) {
LocalOrImport::Local(local_table_index) => ( LocalOrImport::Local(local_table_index) => (
unsafe { unsafe {
cache_builder.build_struct_gep( cache_builder.build_struct_gep(
@ -782,6 +809,7 @@ impl<'a> CtxType<'a> {
) )
}, },
local_table_index.index() as u64, local_table_index.index() as u64,
"context_field_ptr_to_local_table",
), ),
LocalOrImport::Import(import_table_index) => ( LocalOrImport::Import(import_table_index) => (
unsafe { unsafe {
@ -792,12 +820,20 @@ impl<'a> CtxType<'a> {
) )
}, },
import_table_index.index() as u64, import_table_index.index() as u64,
"context_field_ptr_to_import_table",
), ),
}; };
let table_array_ptr = cache_builder let table_array_ptr = cache_builder
.build_load(table_array_ptr_ptr, "table_array_ptr") .build_load(table_array_ptr_ptr, "table_array_ptr")
.into_pointer_value(); .into_pointer_value();
tbaa_label(
module.clone(),
intrinsics,
field_name,
table_array_ptr.as_instruction_value().unwrap(),
None,
);
let const_index = intrinsics.i32_ty.const_int(index, false); let const_index = intrinsics.i32_ty.const_int(index, false);
let table_ptr_ptr = unsafe { let table_ptr_ptr = unsafe {
cache_builder.build_in_bounds_gep(table_array_ptr, &[const_index], "table_ptr_ptr") cache_builder.build_in_bounds_gep(table_array_ptr, &[const_index], "table_ptr_ptr")
@ -805,6 +841,13 @@ impl<'a> CtxType<'a> {
let table_ptr = cache_builder let table_ptr = cache_builder
.build_load(table_ptr_ptr, "table_ptr") .build_load(table_ptr_ptr, "table_ptr")
.into_pointer_value(); .into_pointer_value();
tbaa_label(
module.clone(),
intrinsics,
"table_ptr",
table_array_ptr.as_instruction_value().unwrap(),
Some(index as u32),
);
let (ptr_to_base_ptr, ptr_to_bounds) = unsafe { let (ptr_to_base_ptr, ptr_to_bounds) = unsafe {
( (
@ -826,15 +869,30 @@ impl<'a> CtxType<'a> {
&mut self, &mut self,
index: TableIndex, index: TableIndex,
intrinsics: &Intrinsics, intrinsics: &Intrinsics,
module: Rc<RefCell<Module>>,
builder: &Builder, builder: &Builder,
) -> (PointerValue, IntValue) { ) -> (PointerValue, IntValue) {
let (ptr_to_base_ptr, ptr_to_bounds) = self.table_prepare(index, intrinsics); let (ptr_to_base_ptr, ptr_to_bounds) =
( self.table_prepare(index, intrinsics, module.clone());
builder let base_ptr = builder
.build_load(ptr_to_base_ptr, "base_ptr") .build_load(ptr_to_base_ptr, "base_ptr")
.into_pointer_value(), .into_pointer_value();
builder.build_load(ptr_to_bounds, "bounds").into_int_value(), let bounds = builder.build_load(ptr_to_bounds, "bounds").into_int_value();
) tbaa_label(
module.clone(),
intrinsics,
"table_base_ptr",
base_ptr.as_instruction_value().unwrap(),
Some(index.index() as u32),
);
tbaa_label(
module.clone(),
intrinsics,
"table_bounds",
bounds.as_instruction_value().unwrap(),
Some(index.index() as u32),
);
(base_ptr, bounds)
} }
pub fn local_func( pub fn local_func(
@ -842,6 +900,7 @@ impl<'a> CtxType<'a> {
index: LocalFuncIndex, index: LocalFuncIndex,
fn_ty: FunctionType, fn_ty: FunctionType,
intrinsics: &Intrinsics, intrinsics: &Intrinsics,
module: Rc<RefCell<Module>>,
builder: &Builder, builder: &Builder,
) -> PointerValue { ) -> PointerValue {
let local_func_array_ptr_ptr = unsafe { let local_func_array_ptr_ptr = unsafe {
@ -854,6 +913,13 @@ impl<'a> CtxType<'a> {
let local_func_array_ptr = builder let local_func_array_ptr = builder
.build_load(local_func_array_ptr_ptr, "local_func_array_ptr") .build_load(local_func_array_ptr_ptr, "local_func_array_ptr")
.into_pointer_value(); .into_pointer_value();
tbaa_label(
module.clone(),
intrinsics,
"context_field_ptr_to_local_funcs",
local_func_array_ptr.as_instruction_value().unwrap(),
None,
);
let local_func_ptr_ptr = unsafe { let local_func_ptr_ptr = unsafe {
builder.build_in_bounds_gep( builder.build_in_bounds_gep(
local_func_array_ptr, local_func_array_ptr,
@ -864,6 +930,13 @@ impl<'a> CtxType<'a> {
let local_func_ptr = builder let local_func_ptr = builder
.build_load(local_func_ptr_ptr, "local_func_ptr") .build_load(local_func_ptr_ptr, "local_func_ptr")
.into_pointer_value(); .into_pointer_value();
tbaa_label(
module.clone(),
intrinsics,
"local_func_ptr",
local_func_ptr.as_instruction_value().unwrap(),
Some(index.index() as u32),
);
builder.build_pointer_cast( builder.build_pointer_cast(
local_func_ptr, local_func_ptr,
fn_ty.ptr_type(AddressSpace::Generic), fn_ty.ptr_type(AddressSpace::Generic),
@ -905,7 +978,12 @@ impl<'a> CtxType<'a> {
}) })
} }
pub fn global_cache(&mut self, index: GlobalIndex, intrinsics: &Intrinsics) -> GlobalCache { pub fn global_cache(
&mut self,
index: GlobalIndex,
intrinsics: &Intrinsics,
module: Rc<RefCell<Module>>,
) -> GlobalCache {
let (cached_globals, ctx_ptr_value, info, cache_builder) = ( let (cached_globals, ctx_ptr_value, info, cache_builder) = (
&mut self.cached_globals, &mut self.cached_globals,
self.ctx_ptr_value, self.ctx_ptr_value,
@ -914,7 +992,7 @@ impl<'a> CtxType<'a> {
); );
*cached_globals.entry(index).or_insert_with(|| { *cached_globals.entry(index).or_insert_with(|| {
let (globals_array_ptr_ptr, index, mutable, wasmer_ty) = let (globals_array_ptr_ptr, index, mutable, wasmer_ty, field_name) =
match index.local_or_import(info) { match index.local_or_import(info) {
LocalOrImport::Local(local_global_index) => { LocalOrImport::Local(local_global_index) => {
let desc = info.globals[local_global_index].desc; let desc = info.globals[local_global_index].desc;
@ -929,6 +1007,7 @@ impl<'a> CtxType<'a> {
local_global_index.index() as u64, local_global_index.index() as u64,
desc.mutable, desc.mutable,
desc.ty, desc.ty,
"context_field_ptr_to_local_globals",
) )
} }
LocalOrImport::Import(import_global_index) => { LocalOrImport::Import(import_global_index) => {
@ -944,6 +1023,7 @@ impl<'a> CtxType<'a> {
import_global_index.index() as u64, import_global_index.index() as u64,
desc.mutable, desc.mutable,
desc.ty, desc.ty,
"context_field_ptr_to_imported_globals",
) )
} }
}; };
@ -953,6 +1033,13 @@ impl<'a> CtxType<'a> {
let global_array_ptr = cache_builder let global_array_ptr = cache_builder
.build_load(globals_array_ptr_ptr, "global_array_ptr") .build_load(globals_array_ptr_ptr, "global_array_ptr")
.into_pointer_value(); .into_pointer_value();
tbaa_label(
module.clone(),
intrinsics,
field_name,
global_array_ptr.as_instruction_value().unwrap(),
None,
);
let const_index = intrinsics.i32_ty.const_int(index, false); let const_index = intrinsics.i32_ty.const_int(index, false);
let global_ptr_ptr = unsafe { let global_ptr_ptr = unsafe {
cache_builder.build_in_bounds_gep( cache_builder.build_in_bounds_gep(
@ -964,6 +1051,13 @@ impl<'a> CtxType<'a> {
let global_ptr = cache_builder let global_ptr = cache_builder
.build_load(global_ptr_ptr, "global_ptr") .build_load(global_ptr_ptr, "global_ptr")
.into_pointer_value(); .into_pointer_value();
tbaa_label(
module.clone(),
intrinsics,
"global_ptr",
global_ptr.as_instruction_value().unwrap(),
Some(index as u32),
);
let global_ptr_typed = let global_ptr_typed =
cache_builder.build_pointer_cast(global_ptr, llvm_ptr_ty, "global_ptr_typed"); cache_builder.build_pointer_cast(global_ptr, llvm_ptr_ty, "global_ptr_typed");
@ -973,9 +1067,15 @@ impl<'a> CtxType<'a> {
ptr_to_value: global_ptr_typed, ptr_to_value: global_ptr_typed,
} }
} else { } else {
GlobalCache::Const { let value = cache_builder.build_load(global_ptr_typed, "global_value");
value: cache_builder.build_load(global_ptr_typed, "global_value"), tbaa_label(
} module.clone(),
intrinsics,
"global",
value.as_instruction_value().unwrap(),
Some(index as u32),
);
GlobalCache::Const { value }
} }
}) })
} }
@ -984,6 +1084,7 @@ impl<'a> CtxType<'a> {
&mut self, &mut self,
index: ImportedFuncIndex, index: ImportedFuncIndex,
intrinsics: &Intrinsics, intrinsics: &Intrinsics,
module: Rc<RefCell<Module>>,
) -> (PointerValue, PointerValue) { ) -> (PointerValue, PointerValue) {
let (cached_imported_functions, ctx_ptr_value, cache_builder) = ( let (cached_imported_functions, ctx_ptr_value, cache_builder) = (
&mut self.cached_imported_functions, &mut self.cached_imported_functions,
@ -1002,6 +1103,13 @@ impl<'a> CtxType<'a> {
let func_array_ptr = cache_builder let func_array_ptr = cache_builder
.build_load(func_array_ptr_ptr, "func_array_ptr") .build_load(func_array_ptr_ptr, "func_array_ptr")
.into_pointer_value(); .into_pointer_value();
tbaa_label(
module.clone(),
intrinsics,
"context_field_ptr_to_imported_funcs",
func_array_ptr.as_instruction_value().unwrap(),
None,
);
let const_index = intrinsics.i32_ty.const_int(index.index() as u64, false); let const_index = intrinsics.i32_ty.const_int(index.index() as u64, false);
let imported_func_ptr = unsafe { let imported_func_ptr = unsafe {
cache_builder.build_in_bounds_gep( cache_builder.build_in_bounds_gep(
@ -1023,6 +1131,20 @@ impl<'a> CtxType<'a> {
let ctx_ptr = cache_builder let ctx_ptr = cache_builder
.build_load(ctx_ptr_ptr, "ctx_ptr") .build_load(ctx_ptr_ptr, "ctx_ptr")
.into_pointer_value(); .into_pointer_value();
tbaa_label(
module.clone(),
intrinsics,
"imported_func_ptr",
func_ptr.as_instruction_value().unwrap(),
Some(index.index() as u32),
);
tbaa_label(
module.clone(),
intrinsics,
"imported_func_ctx_ptr",
ctx_ptr.as_instruction_value().unwrap(),
Some(index.index() as u32),
);
ImportedFuncCache { func_ptr, ctx_ptr } ImportedFuncCache { func_ptr, ctx_ptr }
}); });
@ -1034,6 +1156,7 @@ impl<'a> CtxType<'a> {
&mut self, &mut self,
index: usize, index: usize,
intrinsics: &Intrinsics, intrinsics: &Intrinsics,
module: Rc<RefCell<Module>>,
builder: &Builder, builder: &Builder,
) -> PointerValue { ) -> PointerValue {
assert!(index < INTERNALS_SIZE); assert!(index < INTERNALS_SIZE);
@ -1048,6 +1171,13 @@ impl<'a> CtxType<'a> {
let local_internals_ptr = builder let local_internals_ptr = builder
.build_load(local_internals_ptr_ptr, "local_internals_ptr") .build_load(local_internals_ptr_ptr, "local_internals_ptr")
.into_pointer_value(); .into_pointer_value();
tbaa_label(
module.clone(),
intrinsics,
"context_field_ptr_to_internals",
local_internals_ptr_ptr.as_instruction_value().unwrap(),
None,
);
unsafe { unsafe {
builder.build_in_bounds_gep( builder.build_in_bounds_gep(
local_internals_ptr, local_internals_ptr,
@ -1057,3 +1187,83 @@ impl<'a> CtxType<'a> {
} }
} }
} }
// Given an instruction that operates on memory, mark the access as not aliasing
// other memory accesses which have a different (label, index) pair.
pub fn tbaa_label(
module: Rc<RefCell<Module>>,
intrinsics: &Intrinsics,
label: &str,
instruction: InstructionValue,
index: Option<u32>,
) {
// To convey to LLVM that two pointers must be pointing to distinct memory,
// we use LLVM's Type Based Aliasing Analysis, or TBAA, to mark the memory
// operations as having different types whose pointers may not alias.
//
// See the LLVM documentation at
// https://llvm.org/docs/LangRef.html#tbaa-metadata
//
// LLVM TBAA supports many features, but we use it in a simple way, with
// only scalar types that are children of the root node. Every TBAA type we
// declare is NoAlias with the others. See NoAlias, PartialAlias,
// MayAlias and MustAlias in the LLVM documentation:
// https://llvm.org/docs/AliasAnalysis.html#must-may-and-no-alias-responses
let module = module.borrow_mut();
let context = module.get_context();
// `!wasmer_tbaa_root = {}`, the TBAA root node for wasmer.
let tbaa_root = module
.get_global_metadata("wasmer_tbaa_root")
.pop()
.unwrap_or_else(|| {
module.add_global_metadata("wasmer_tbaa_root", &context.metadata_node(&[]));
module.get_global_metadata("wasmer_tbaa_root")[0]
});
// Construct (or look up) the type descriptor, for example
// `!"local 0" = !{!"local 0", !wasmer_tbaa_root}`.
let label = if let Some(idx) = index {
format!("{}{}", label, idx)
} else {
label.to_string()
};
let type_label = context.metadata_string(label.as_str());
let type_tbaa = module
.get_global_metadata(label.as_str())
.pop()
.unwrap_or_else(|| {
module.add_global_metadata(
label.as_str(),
&context.metadata_node(&[type_label.into(), tbaa_root.into()]),
);
module.get_global_metadata(label.as_str())[0]
});
// Construct (or look up) the access tag, which is a struct of the form
// (base type, access type, offset).
//
// "If BaseTy is a scalar type, Offset must be 0 and BaseTy and AccessTy
// must be the same".
// -- https://llvm.org/docs/LangRef.html#tbaa-metadata
let label = label + "_memop";
let type_tbaa = module
.get_global_metadata(label.as_str())
.pop()
.unwrap_or_else(|| {
module.add_global_metadata(
label.as_str(),
&context.metadata_node(&[
type_tbaa.into(),
type_tbaa.into(),
intrinsics.i64_zero.into(),
]),
);
module.get_global_metadata(label.as_str())[0]
});
// Attach the access tag to the instruction.
let tbaa_kind = context.get_kind_id("tbaa");
instruction.set_metadata(type_tbaa, tbaa_kind);
}

View File

@ -67,9 +67,29 @@ impl ControlFrame {
} }
} }
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
pub enum ExtraInfo {
None,
// This values is required to be arithmetic 32-bit NaN (or 32x4) by the WAsm
// machine, but which might not be in the LLVM value. The conversion to
// arithmetic NaN is pending. It is required for correctness.
PendingF32NaN,
// This values is required to be arithmetic 64-bit NaN (or 64x2) by the WAsm
// machine, but which might not be in the LLVM value. The conversion to
// arithmetic NaN is pending. It is required for correctness.
PendingF64NaN,
}
impl Default for ExtraInfo {
fn default() -> Self {
ExtraInfo::None
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct State { pub struct State {
pub stack: Vec<BasicValueEnum>, pub stack: Vec<(BasicValueEnum, ExtraInfo)>,
control_stack: Vec<ControlFrame>, control_stack: Vec<ControlFrame>,
value_counter: Cell<usize>, value_counter: Cell<usize>,
@ -145,10 +165,18 @@ impl State {
} }
pub fn push1<T: BasicValue>(&mut self, value: T) { pub fn push1<T: BasicValue>(&mut self, value: T) {
self.stack.push(value.as_basic_value_enum()) self.push1_extra(value, ExtraInfo::None);
}
pub fn push1_extra<T: BasicValue>(&mut self, value: T, info: ExtraInfo) {
self.stack.push((value.as_basic_value_enum(), info));
} }
pub fn pop1(&mut self) -> Result<BasicValueEnum, BinaryReaderError> { pub fn pop1(&mut self) -> Result<BasicValueEnum, BinaryReaderError> {
Ok(self.pop1_extra()?.0)
}
pub fn pop1_extra(&mut self) -> Result<(BasicValueEnum, ExtraInfo), BinaryReaderError> {
self.stack.pop().ok_or(BinaryReaderError { self.stack.pop().ok_or(BinaryReaderError {
message: "invalid value stack", message: "invalid value stack",
offset: -1isize as usize, offset: -1isize as usize,
@ -161,6 +189,14 @@ impl State {
Ok((v1, v2)) Ok((v1, v2))
} }
pub fn pop2_extra(
&mut self,
) -> Result<((BasicValueEnum, ExtraInfo), (BasicValueEnum, ExtraInfo)), BinaryReaderError> {
let v2 = self.pop1_extra()?;
let v1 = self.pop1_extra()?;
Ok((v1, v2))
}
pub fn pop3( pub fn pop3(
&mut self, &mut self,
) -> Result<(BasicValueEnum, BasicValueEnum, BasicValueEnum), BinaryReaderError> { ) -> Result<(BasicValueEnum, BasicValueEnum, BasicValueEnum), BinaryReaderError> {
@ -170,7 +206,23 @@ impl State {
Ok((v1, v2, v3)) Ok((v1, v2, v3))
} }
pub fn peek1(&self) -> Result<BasicValueEnum, BinaryReaderError> { pub fn pop3_extra(
&mut self,
) -> Result<
(
(BasicValueEnum, ExtraInfo),
(BasicValueEnum, ExtraInfo),
(BasicValueEnum, ExtraInfo),
),
BinaryReaderError,
> {
let v3 = self.pop1_extra()?;
let v2 = self.pop1_extra()?;
let v1 = self.pop1_extra()?;
Ok((v1, v2, v3))
}
pub fn peek1_extra(&self) -> Result<(BasicValueEnum, ExtraInfo), BinaryReaderError> {
self.stack self.stack
.get(self.stack.len() - 1) .get(self.stack.len() - 1)
.ok_or(BinaryReaderError { .ok_or(BinaryReaderError {
@ -180,7 +232,14 @@ impl State {
.map(|v| *v) .map(|v| *v)
} }
pub fn peekn(&self, n: usize) -> Result<&[BasicValueEnum], BinaryReaderError> { pub fn peekn(&self, n: usize) -> Result<Vec<BasicValueEnum>, BinaryReaderError> {
Ok(self.peekn_extra(n)?.iter().map(|x| x.0).collect())
}
pub fn peekn_extra(
&self,
n: usize,
) -> Result<&[(BasicValueEnum, ExtraInfo)], BinaryReaderError> {
self.stack self.stack
.get(self.stack.len() - n..) .get(self.stack.len() - n..)
.ok_or(BinaryReaderError { .ok_or(BinaryReaderError {
@ -189,8 +248,11 @@ impl State {
}) })
} }
pub fn popn_save(&mut self, n: usize) -> Result<Vec<BasicValueEnum>, BinaryReaderError> { pub fn popn_save_extra(
let v = self.peekn(n)?.to_vec(); &mut self,
n: usize,
) -> Result<Vec<(BasicValueEnum, ExtraInfo)>, BinaryReaderError> {
let v = self.peekn_extra(n)?.to_vec();
self.popn(n)?; self.popn(n)?;
Ok(v) Ok(v)
} }

View File

@ -20,7 +20,7 @@ pub fn generate_trampolines(
context: &Context, context: &Context,
builder: &Builder, builder: &Builder,
intrinsics: &Intrinsics, intrinsics: &Intrinsics,
) { ) -> Result<(), String> {
for (sig_index, sig) in info.signatures.iter() { for (sig_index, sig) in info.signatures.iter() {
let func_type = signatures[sig_index]; let func_type = signatures[sig_index];
@ -42,8 +42,9 @@ pub fn generate_trampolines(
Some(Linkage::External), Some(Linkage::External),
); );
generate_trampoline(trampoline_func, sig, context, builder, intrinsics); generate_trampoline(trampoline_func, sig, context, builder, intrinsics)?;
} }
Ok(())
} }
fn generate_trampoline( fn generate_trampoline(
@ -52,7 +53,7 @@ fn generate_trampoline(
context: &Context, context: &Context,
builder: &Builder, builder: &Builder,
intrinsics: &Intrinsics, intrinsics: &Intrinsics,
) { ) -> 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);
@ -64,7 +65,7 @@ fn generate_trampoline(
args_ptr.into_pointer_value(), args_ptr.into_pointer_value(),
returns_ptr.into_pointer_value(), returns_ptr.into_pointer_value(),
), ),
_ => unimplemented!(), _ => return Err("trampoline function unimplemented".to_string()),
}; };
let cast_ptr_ty = |wasmer_ty| match wasmer_ty { let cast_ptr_ty = |wasmer_ty| match wasmer_ty {
@ -108,8 +109,11 @@ fn generate_trampoline(
call_site.try_as_basic_value().left().unwrap(), call_site.try_as_basic_value().left().unwrap(),
); );
} }
_ => unimplemented!("multi-value returns"), _ => {
return Err("trampoline function multi-value returns unimplemented".to_string());
}
} }
builder.build_return(None); builder.build_return(None);
Ok(())
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "wasmer-middleware-common-tests" name = "wasmer-middleware-common-tests"
version = "0.6.0" version = "0.9.0"
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.6.0" } wasmer-runtime-core = { path = "../runtime-core", version = "0.9.0" }
wasmer-middleware-common = { path = "../middleware-common", version = "0.6.0" } wasmer-middleware-common = { path = "../middleware-common", version = "0.9.0" }
wasmer-clif-backend = { path = "../clif-backend", version = "0.6.0" } wasmer-clif-backend = { path = "../clif-backend", version = "0.9.0" }
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.6.0", optional = true } wasmer-llvm-backend = { path = "../llvm-backend", version = "0.9.0", optional = true }
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.6.0", optional = true } wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.9.0", optional = true }
[features] [features]
clif = [] clif = []

View File

@ -1,6 +1,6 @@
[package] [package]
name = "wasmer-middleware-common" name = "wasmer-middleware-common"
version = "0.6.0" version = "0.9.0"
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"
@ -8,4 +8,4 @@ authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
wasmer-runtime-core = { path = "../runtime-core" } wasmer-runtime-core = { path = "../runtime-core", version = "0.9.0" }

View File

@ -1,27 +0,0 @@
[package]
name = "wasmer-runtime-abi"
version = "0.6.0"
description = "Wasmer runtime core library"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
repository = "https://github.com/wasmerio/wasmer"
edition = "2018"
[dependencies]
libc = "0.2.60"
wasmer-runtime-core = { path = "../runtime-core" }
failure = "0.1"
tar = "0.4"
wasmparser = "0.35.1"
zstd = "0.4"
# [target.'cfg(unix)'.dependencies.zbox]
# git = "https://github.com/wasmerio/zbox"
# branch = "bundle-libsodium"
# features = ["libsodium-bundled"]
[dev-dependencies]
tempdir = "0.3"
[features]
debug = []

View File

@ -1,23 +0,0 @@
# runtime-abi
This crate has ABI functions (like syscalls) and extensions to the runtime for enabling ABIs (e.g. virtual filesystem).
## Virtual Filesystem (experimental)
The virtual filesystem allows the runtime to read bundled wasm data as if they were files. Data that is stored in a
custom section compressed with [zstd][1] compression and archived with [tar][2] will be exposed as files and mounted
in the `/` root.
The only current supported operation is the `read` syscall.
The virtual filesystem is not enabled by default. Build with `--features vfs` to use it.
[Zbox][3] is a virtual filesystem that depends on [libsodium][4]. See [installation instructions][5] for libsodium here. One can
statically link libsodium with the [instructions][6] on Zbox's readme.
[1]: https://facebook.github.io/zstd/
[2]: https://www.gnu.org/software/tar/
[3]: https://zbox.io/
[4]: https://download.libsodium.org/doc/
[5]: https://download.libsodium.org/doc/installation
[6]: https://github.com/zboxfs/zbox#static-linking-with-libsodium

View File

@ -1,11 +0,0 @@
#![deny(dead_code, unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
#![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")]
#![doc(html_logo_url = "https://avatars3.githubusercontent.com/u/44205449?s=200&v=4")]
#[cfg(not(target_os = "windows"))]
#[macro_use]
extern crate failure;
#[cfg(not(target_os = "windows"))]
pub mod vfs;

View File

@ -1,111 +0,0 @@
use crate::vfs::file_like::{FileLike, Metadata};
use failure::Error;
use std::io;
pub struct Stdin;
pub struct Stdout;
pub struct Stderr;
impl FileLike for Stdin {
fn metadata(&self) -> Result<Metadata, Error> {
unimplemented!()
}
fn set_file_len(&mut self, _len: usize) -> Result<(), failure::Error> {
panic!("Cannot set length of stdin");
}
}
impl io::Read for Stdin {
fn read(&mut self, _buf: &mut [u8]) -> Result<usize, io::Error> {
unimplemented!()
}
}
impl io::Write for Stdin {
fn write(&mut self, _buf: &[u8]) -> Result<usize, io::Error> {
unimplemented!()
}
fn flush(&mut self) -> Result<(), io::Error> {
unimplemented!()
}
}
impl io::Seek for Stdin {
fn seek(&mut self, _pos: io::SeekFrom) -> Result<u64, io::Error> {
unimplemented!()
}
}
impl FileLike for Stdout {
fn metadata(&self) -> Result<Metadata, failure::Error> {
unimplemented!()
}
fn set_file_len(&mut self, _len: usize) -> Result<(), failure::Error> {
panic!("Cannot set length of stdout");
}
}
impl io::Read for Stdout {
fn read(&mut self, _buf: &mut [u8]) -> Result<usize, io::Error> {
unimplemented!()
}
}
impl io::Write for Stdout {
fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
let stdout = io::stdout();
let mut handle = stdout.lock();
handle.write(buf)
}
fn flush(&mut self) -> Result<(), io::Error> {
let stdout = io::stdout();
let mut handle = stdout.lock();
handle.flush()
}
}
impl io::Seek for Stdout {
fn seek(&mut self, _pos: io::SeekFrom) -> Result<u64, io::Error> {
unimplemented!()
}
}
impl FileLike for Stderr {
fn metadata(&self) -> Result<Metadata, failure::Error> {
unimplemented!()
}
fn set_file_len(&mut self, _len: usize) -> Result<(), failure::Error> {
panic!("Cannot set length of stderr");
}
}
impl io::Read for Stderr {
fn read(&mut self, _buf: &mut [u8]) -> Result<usize, io::Error> {
unimplemented!()
}
}
impl io::Write for Stderr {
fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
let stderr = io::stderr();
let mut handle = stderr.lock();
handle.write(buf)
}
fn flush(&mut self) -> Result<(), io::Error> {
let stderr = io::stderr();
let mut handle = stderr.lock();
handle.flush()
}
}
impl io::Seek for Stderr {
fn seek(&mut self, _pos: io::SeekFrom) -> Result<u64, io::Error> {
unimplemented!()
}
}

View File

@ -1,21 +0,0 @@
pub type Fd = isize;
#[derive(Debug)]
pub struct Metadata {
pub len: usize,
pub is_file: bool,
}
pub trait FileLike: std::io::Write + std::io::Read + std::io::Seek {
// get metadata
fn metadata(&self) -> Result<Metadata, failure::Error>;
// write
// fn write_file(&mut self, buf: &[u8]) -> Result<usize, io::Error>;
// read
// fn read_file(&mut self, buf: &mut [u8]) -> Result<usize, io::Error>;
// set_file_len
fn set_file_len(&mut self, len: usize) -> Result<(), failure::Error>;
}

View File

@ -1,5 +0,0 @@
pub mod device_file;
pub mod file_like;
pub mod vfs;
pub mod vfs_header;
pub mod virtual_file;

View File

@ -1,170 +0,0 @@
use crate::vfs::file_like::FileLike;
use crate::vfs::vfs_header::{header_from_bytes, ArchiveType, CompressionType};
use crate::vfs::virtual_file::VirtualFile;
use std::collections::HashMap;
use std::cell::RefCell;
use std::io;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use tar::EntryType;
use zbox::{init_env, OpenOptions, Repo, RepoOpener};
pub struct Vfs {
repo: Repo,
device_files: HashMap<PathBuf, Rc<RefCell<dyn FileLike>>>,
}
impl Vfs {
/// Like `VfsBacking::from_tar_bytes` except it also decompresses from the zstd format.
pub fn from_tar_zstd_bytes<Reader: Read>(tar_bytes: Reader) -> Result<Self, failure::Error> {
let result = zstd::decode_all(tar_bytes);
let decompressed_data = result.unwrap();
Self::from_tar_bytes(&decompressed_data[..])
}
/// Match on the type of the compressed-archive and select the correct unpack method
pub fn from_compressed_bytes(compressed_data_slice: &[u8]) -> Result<Self, failure::Error> {
let data_bytes = &compressed_data_slice[4..];
match header_from_bytes(compressed_data_slice)? {
(_, CompressionType::ZSTD, ArchiveType::TAR) => Self::from_tar_zstd_bytes(data_bytes),
(_, CompressionType::NONE, ArchiveType::TAR) => Self::from_tar_bytes(data_bytes),
}
}
/// Create a vfs from raw bytes in tar format
pub fn from_tar_bytes<Reader: Read>(tar_bytes: Reader) -> Result<Self, failure::Error> {
init_env();
let mut repo = RepoOpener::new()
.create(true)
.open("mem://wasmer_fs", "")
.unwrap();
let _errors = tar::Archive::new(tar_bytes)
.entries()?
.map(|entry| {
let mut entry: tar::Entry<Reader> = entry?;
let path = entry.path()?;
let path = convert_to_absolute_path(path);
let _result = match (entry.header().entry_type(), path.parent()) {
(EntryType::Regular, Some(parent)) => {
if let Err(e) = repo.create_dir_all(parent) {
if e == zbox::Error::AlreadyExists || e == zbox::Error::IsRoot {
} else {
return Err(VfsAggregateError::ZboxError(e));
}
} else {
}
let mut file = repo.create_file(&path)?;
if entry.header().size().unwrap_or(0) > 0 {
io::copy(&mut entry, &mut file)?;
file.finish()?;
}
}
(EntryType::Directory, _) => {
if let Err(e) = repo.create_dir_all(path) {
if e == zbox::Error::AlreadyExists || e == zbox::Error::IsRoot {
} else {
return Err(VfsAggregateError::ZboxError(e));
}
} else {
}
}
_ => return Err(VfsAggregateError::UnsupportedFileType),
};
Ok(())
})
.collect::<Vec<Result<(), VfsAggregateError>>>();
// let import_errors = errors.iter().filter_map(|e| e.err()).collect::<Vec<_>>();
let vfs = Self {
repo,
device_files: HashMap::new(),
// import_errors: vec![],
};
Ok(vfs)
}
pub fn new() -> Result<(Self, Vec<VfsError>), failure::Error> {
init_env();
let repo = RepoOpener::new()
.create(true)
.open("mem://wasmer_fs", "")
.unwrap();
Ok((
Vfs {
repo,
device_files: HashMap::new(),
},
vec![],
))
}
pub fn open_file<P: AsRef<Path>>(&mut self, path: P) -> Option<Rc<RefCell<dyn FileLike>>> {
init_env();
let path = convert_to_absolute_path(path);
if let Ok(file) = OpenOptions::new().write(true).open(&mut self.repo, &path) {
Some(Rc::new(RefCell::new(VirtualFile::new(file))))
} else if let Some(dev_file) = self.device_files.get(&path) {
Some(dev_file.clone())
} else {
None
}
}
pub fn make_dir<P: AsRef<Path>>(&mut self, path: P) {
self.repo.create_dir_all(path).unwrap();
}
pub fn create_device_file<P: AsRef<Path>>(&mut self, path: P, file: Rc<RefCell<dyn FileLike>>) {
self.device_files.insert(path.as_ref().to_path_buf(), file);
}
}
fn convert_to_absolute_path<P: AsRef<Path>>(path: P) -> PathBuf {
let path = path.as_ref();
if path.is_relative() {
std::path::PathBuf::from("/").join(path)
} else {
path.to_path_buf()
}
}
pub type Handle = i32;
#[derive(Debug, Fail)]
pub enum VfsError {
#[fail(display = "File with file descriptor \"{}\" does not exist.", _0)]
FileWithFileDescriptorNotExist(Handle),
#[fail(display = "File descriptor does not exist.")]
FileDescriptorNotExist(Handle),
#[fail(display = "Source file descriptor does not exist.")]
SourceFileDescriptorDoesNotExist,
#[fail(display = "Target file descriptor already exists.")]
TargetFileDescriptorAlreadyExists,
#[fail(display = "Could not get a mutable reference to the file because it is in use.")]
CouldNotGetMutableReferenceToFile,
}
#[derive(Debug, Fail)]
pub enum VfsAggregateError {
#[fail(display = "Entry error.")]
EntryError(std::io::Error),
#[fail(display = "IO error.")]
IoError(std::io::Error),
#[fail(display = "Zbox error.")]
ZboxError(zbox::Error),
#[fail(display = "Unsupported file type.")]
UnsupportedFileType,
}
impl std::convert::From<std::io::Error> for VfsAggregateError {
fn from(error: std::io::Error) -> VfsAggregateError {
VfsAggregateError::EntryError(error)
}
}
impl std::convert::From<zbox::Error> for VfsAggregateError {
fn from(error: zbox::Error) -> VfsAggregateError {
VfsAggregateError::ZboxError(error)
}
}

View File

@ -1,57 +0,0 @@
/// Represents the version of this header schema.
#[repr(u8)]
#[derive(Debug, PartialEq)]
pub enum HeaderVersion {
Version1 = 1,
}
/// Represents the compression type of the file data. Only Zstd or no-compression is supported.
#[repr(u8)]
#[derive(Debug, PartialEq)]
pub enum CompressionType {
NONE = 0,
ZSTD = 1,
}
/// Represents the type of archive. The only supported archive is the Tar format.
#[repr(u8)]
#[derive(Debug, PartialEq)]
pub enum ArchiveType {
TAR = 0,
}
// extract the header data from bytes
pub fn header_from_bytes(
bytes: &[u8],
) -> Result<(HeaderVersion, CompressionType, ArchiveType), HeaderError> {
if let Some(bytes) = bytes.get(..4) {
let version = match bytes[0] {
1 => HeaderVersion::Version1,
x => return Err(HeaderError::UnknownHeaderVersion(x)),
};
let compression_type = match bytes[1] {
0 => CompressionType::NONE,
1 => CompressionType::ZSTD,
x => return Err(HeaderError::UnknownCompressionType(x)),
};
let archive_type = match bytes[2] {
0 => ArchiveType::TAR,
x => return Err(HeaderError::UnknownArchiveType(x)),
};
Ok((version, compression_type, archive_type))
} else {
Err(HeaderError::HeaderTooSmall)
}
}
#[derive(Debug, Fail)]
pub enum HeaderError {
#[fail(display = "The version is not supported: \"{}\"", _0)]
UnknownHeaderVersion(u8),
#[fail(display = "The compression type is unknown: \"{}\"", _0)]
UnknownCompressionType(u8),
#[fail(display = "The archive type is unknown: \"{}\"", _0)]
UnknownArchiveType(u8),
#[fail(display = "The header is too small.")]
HeaderTooSmall,
}

View File

@ -1,51 +0,0 @@
use crate::vfs::file_like::{FileLike, Metadata};
use failure::Error;
use std::io;
pub struct VirtualFile(zbox::File);
impl VirtualFile {
pub fn new(file: zbox::File) -> Self {
VirtualFile(file)
}
}
impl FileLike for VirtualFile {
fn metadata(&self) -> Result<Metadata, Error> {
self.0
.metadata()
.map(|m| Metadata {
len: m.content_len(),
is_file: m.is_file(),
})
.map_err(|e: zbox::Error| e.into())
}
fn set_file_len(&mut self, len: usize) -> Result<(), failure::Error> {
self.0.set_len(len).map_err(|e| e.into())
}
}
impl io::Write for VirtualFile {
fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
let result = self.0.write(buf)?;
self.0.finish().unwrap();
Ok(result)
}
fn flush(&mut self) -> Result<(), io::Error> {
self.0.flush()
}
}
impl io::Read for VirtualFile {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
self.0.read(buf)
}
}
impl io::Seek for VirtualFile {
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, io::Error> {
self.0.seek(pos)
}
}

View File

@ -1,6 +1,6 @@
[package] [package]
name = "wasmer-runtime-c-api" name = "wasmer-runtime-c-api"
version = "0.6.0" version = "0.9.0"
description = "Wasmer C API library" description = "Wasmer C API library"
license = "MIT" license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"] authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -17,19 +17,26 @@ libc = "0.2.60"
[dependencies.wasmer-runtime] [dependencies.wasmer-runtime]
default-features = false default-features = false
path = "../runtime" path = "../runtime"
version = "0.6.0" version = "0.9.0"
[dependencies.wasmer-runtime-core] [dependencies.wasmer-runtime-core]
default-features = false default-features = false
path = "../runtime-core" path = "../runtime-core"
version = "0.6.0" version = "0.9.0"
[dependencies.wasmer-wasi]
default-features = false
path = "../wasi"
version = "0.9.0"
optional = true
[features] [features]
default = ["cranelift-backend"] default = ["cranelift-backend", "wasi"]
debug = ["wasmer-runtime/debug"] debug = ["wasmer-runtime/debug"]
cranelift-backend = ["wasmer-runtime/cranelift", "wasmer-runtime/default-backend-cranelift"] cranelift-backend = ["wasmer-runtime/cranelift", "wasmer-runtime/default-backend-cranelift"]
llvm-backend = ["wasmer-runtime/llvm", "wasmer-runtime/default-backend-llvm"] llvm-backend = ["wasmer-runtime/llvm", "wasmer-runtime/default-backend-llvm"]
singlepass-backend = ["wasmer-runtime/singlepass", "wasmer-runtime/default-backend-singlepass"] singlepass-backend = ["wasmer-runtime/singlepass", "wasmer-runtime/default-backend-singlepass"]
wasi = ["wasmer-wasi"]
[build-dependencies] [build-dependencies]
cbindgen = "0.9.1" cbindgen = "0.9"

View File

@ -1,21 +1,21 @@
<p align="center"> <p align="center">
<a href="https://wasmer.io" target="_blank" rel="noopener noreferrer"> <a href="https://wasmer.io" target="_blank" rel="noopener noreferrer">
<img width="400" src="https://raw.githubusercontent.com/wasmerio/wasmer/master/logo.png" alt="Wasmer logo"> <img width="300" src="https://raw.githubusercontent.com/wasmerio/wasmer/master/logo.png" alt="Wasmer logo">
</a> </a>
</p> </p>
<p align="center"> <p align="center">
<a href="https://circleci.com/gh/wasmerio/wasmer/"> <a href="https://dev.azure.com/wasmerio/wasmer/_build/latest?definitionId=3&branchName=master">
<img src="https://img.shields.io/circleci/project/github/wasmerio/wasmer/master.svg" alt="Build Status"> <img src="https://img.shields.io/azure-devops/build/wasmerio/wasmer/3.svg?style=flat-square" alt="Build Status">
</a> </a>
<a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE"> <a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/wasmerio/wasmer.svg" alt="License"> <img src="https://img.shields.io/github/license/wasmerio/wasmer.svg?style=flat-square" alt="License">
</a> </a>
<a href="https://spectrum.chat/wasmer"> <a href="https://spectrum.chat/wasmer">
<img src="https://withspectrum.github.io/badge/badge.svg" alt="Join the Wasmer Community"> <img src="https://withspectrum.github.io/badge/badge.svg" alt="Join the Wasmer Community">
</a> </a>
<a href="https://crates.io/crates/wasmer-runtime-c-api"> <a href="https://crates.io/crates/wasmer-runtime-c-api">
<img src="https://img.shields.io/crates/d/wasmer-runtime-c-api.svg" alt="Number of downloads from crates.io"> <img src="https://img.shields.io/crates/d/wasmer-runtime-c-api.svg?style=flat-square" alt="Number of downloads from crates.io">
</a> </a>
<a href="https://docs.rs/wasmer-runtime-c-api"> <a href="https://docs.rs/wasmer-runtime-c-api">
<img src="https://docs.rs/wasmer-runtime-c-api/badge.svg" alt="Read our API documentation"> <img src="https://docs.rs/wasmer-runtime-c-api/badge.svg" alt="Read our API documentation">
@ -25,7 +25,7 @@
# Wasmer Runtime C API # Wasmer Runtime C API
Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully
compatible with Emscripten, Rust and Go. [Learn compatible with WASI, Emscripten, Rust and Go. [Learn
more](https://github.com/wasmerio/wasmer). more](https://github.com/wasmerio/wasmer).
This crate exposes a C and a C++ API for the Wasmer runtime. This crate exposes a C and a C++ API for the Wasmer runtime.
@ -36,6 +36,7 @@ The C and C++ header files can be found in the source tree of this
crate, respectively [`wasmer.h`][wasmer_h] and crate, respectively [`wasmer.h`][wasmer_h] and
[`wasmer.hh`][wasmer_hh]. They are automatically generated, and always [`wasmer.hh`][wasmer_hh]. They are automatically generated, and always
up-to-date in this repository. up-to-date in this repository.
The runtime shared library (so, dll, dylib) can also be downloaded in Wasmer [release page](https://github.com/wasmerio/wasmer/releases).
Here is a simple example to use the C API: Here is a simple example to use the C API:

View File

@ -13,7 +13,7 @@ use crate::{
}; };
use libc::{c_int, c_uint}; use libc::{c_int, c_uint};
use std::{ptr, slice}; use std::{ptr, slice};
use wasmer_runtime::{Instance, Memory, Module, Value}; use wasmer_runtime::{Instance, Module, Value};
use wasmer_runtime_core::{export::Export, module::ExportIndex}; use wasmer_runtime_core::{export::Export, module::ExportIndex};
/// Intermediate representation of an `Export` instance that is /// Intermediate representation of an `Export` instance that is
@ -85,12 +85,41 @@ pub union wasmer_import_export_value {
/// List of export/import kinds. /// List of export/import kinds.
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
#[repr(u32)] #[repr(u32)]
#[derive(Clone)] #[derive(Clone, PartialEq, Eq)]
// ================
// ! DANGER !
// ================
// Do not modify these values without updating the `TryFrom` implementation below
pub enum wasmer_import_export_kind { pub enum wasmer_import_export_kind {
WASM_FUNCTION, WASM_FUNCTION = 0,
WASM_GLOBAL, WASM_GLOBAL = 1,
WASM_MEMORY, WASM_MEMORY = 2,
WASM_TABLE, WASM_TABLE = 3,
}
impl wasmer_import_export_kind {
pub fn to_str(&self) -> &'static str {
match self {
Self::WASM_FUNCTION => "function",
Self::WASM_GLOBAL => "global",
Self::WASM_MEMORY => "memory",
Self::WASM_TABLE => "table",
}
}
}
impl std::convert::TryFrom<u32> for wasmer_import_export_kind {
type Error = ();
fn try_from(value: u32) -> Result<Self, Self::Error> {
Ok(match value {
0 => Self::WASM_FUNCTION,
1 => Self::WASM_GLOBAL,
2 => Self::WASM_MEMORY,
3 => Self::WASM_TABLE,
_ => return Err(()),
})
}
} }
/// Gets export descriptors for the given module /// Gets export descriptors for the given module
@ -355,7 +384,8 @@ pub unsafe extern "C" fn wasmer_export_to_memory(
let export = &named_export.export; let export = &named_export.export;
if let Export::Memory(exported_memory) = export { if let Export::Memory(exported_memory) = export {
*memory = exported_memory as *const Memory as *mut wasmer_memory_t; let mem = Box::new(exported_memory.clone());
*memory = Box::into_raw(mem) as *mut wasmer_memory_t;
wasmer_result_t::WASMER_OK wasmer_result_t::WASMER_OK
} else { } else {
update_last_error(CApiError { update_last_error(CApiError {

View File

@ -9,11 +9,11 @@ use crate::{
wasmer_byte_array, wasmer_result_t, wasmer_byte_array, wasmer_result_t,
}; };
use libc::c_uint; use libc::c_uint;
use std::{ffi::c_void, ptr, slice, sync::Arc}; use std::{convert::TryFrom, ffi::c_void, ptr, slice, sync::Arc};
use wasmer_runtime::{Global, Memory, Module, Table}; use wasmer_runtime::{Global, Memory, Module, Table};
use wasmer_runtime_core::{ use wasmer_runtime_core::{
export::{Context, Export, FuncPointer}, export::{Context, Export, FuncPointer},
import::ImportObject, import::{ImportObject, ImportObjectIterator},
module::ImportName, module::ImportName,
types::{FuncSig, Type}, types::{FuncSig, Type},
}; };
@ -41,6 +41,10 @@ pub struct wasmer_import_descriptor_t;
#[derive(Clone)] #[derive(Clone)]
pub struct wasmer_import_descriptors_t; pub struct wasmer_import_descriptors_t;
#[repr(C)]
#[derive(Clone)]
pub struct wasmer_import_object_iter_t;
/// Creates a new empty import object. /// Creates a new empty import object.
/// See also `wasmer_import_object_append` /// See also `wasmer_import_object_append`
#[allow(clippy::cast_ptr_alignment)] #[allow(clippy::cast_ptr_alignment)]
@ -51,12 +55,318 @@ pub unsafe extern "C" fn wasmer_import_object_new() -> *mut wasmer_import_object
Box::into_raw(import_object) as *mut wasmer_import_object_t Box::into_raw(import_object) as *mut wasmer_import_object_t
} }
#[cfg(feature = "wasi")]
mod wasi;
#[cfg(feature = "wasi")]
pub use self::wasi::*;
/// Gets an entry from an ImportObject at the name and namespace.
/// Stores `name`, `namespace`, and `import_export_value` in `import`.
/// Thus these must remain valid for the lifetime of `import`.
///
/// The caller owns all data involved.
/// `import_export_value` will be written to based on `tag`.
#[no_mangle]
pub unsafe extern "C" fn wasmer_import_object_get_import(
import_object: *const wasmer_import_object_t,
namespace: wasmer_byte_array,
name: wasmer_byte_array,
import: *mut wasmer_import_t,
import_export_value: *mut wasmer_import_export_value,
tag: u32,
) -> wasmer_result_t {
let tag: wasmer_import_export_kind = if let Ok(t) = TryFrom::try_from(tag) {
t
} else {
update_last_error(CApiError {
msg: "wasmer_import_export_tag out of range".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
};
let import_object: &mut ImportObject = &mut *(import_object as *mut ImportObject);
let namespace_str = if let Ok(ns) = namespace.as_str() {
ns
} else {
update_last_error(CApiError {
msg: "error converting namespace to UTF-8 string".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
};
let name_str = if let Ok(name) = name.as_str() {
name
} else {
update_last_error(CApiError {
msg: "error converting name to UTF-8 string".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
};
if import.is_null() || import_export_value.is_null() {
update_last_error(CApiError {
msg: "pointers to import and import_export_value must not be null".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
}
let import_out = &mut *import;
let import_export_value_out = &mut *import_export_value;
if let Some(export) =
import_object.maybe_with_namespace(namespace_str, |ns| ns.get_export(name_str))
{
match export {
Export::Function { .. } => {
if tag != wasmer_import_export_kind::WASM_FUNCTION {
update_last_error(CApiError {
msg: format!("Found function, expected {}", tag.to_str()),
});
return wasmer_result_t::WASMER_ERROR;
}
import_out.tag = wasmer_import_export_kind::WASM_FUNCTION;
let writer = import_export_value_out.func as *mut Export;
*writer = export.clone();
}
Export::Memory(memory) => {
if tag != wasmer_import_export_kind::WASM_MEMORY {
update_last_error(CApiError {
msg: format!("Found memory, expected {}", tag.to_str()),
});
return wasmer_result_t::WASMER_ERROR;
}
import_out.tag = wasmer_import_export_kind::WASM_MEMORY;
let writer = import_export_value_out.func as *mut Memory;
*writer = memory.clone();
}
Export::Table(table) => {
if tag != wasmer_import_export_kind::WASM_TABLE {
update_last_error(CApiError {
msg: format!("Found table, expected {}", tag.to_str()),
});
return wasmer_result_t::WASMER_ERROR;
}
import_out.tag = wasmer_import_export_kind::WASM_TABLE;
let writer = import_export_value_out.func as *mut Table;
*writer = table.clone();
}
Export::Global(global) => {
if tag != wasmer_import_export_kind::WASM_GLOBAL {
update_last_error(CApiError {
msg: format!("Found global, expected {}", tag.to_str()),
});
return wasmer_result_t::WASMER_ERROR;
}
import_out.tag = wasmer_import_export_kind::WASM_GLOBAL;
let writer = import_export_value_out.func as *mut Global;
*writer = global.clone();
}
}
import_out.value = *import_export_value;
import_out.module_name = namespace;
import_out.import_name = name;
wasmer_result_t::WASMER_OK
} else {
update_last_error(CApiError {
msg: format!("Export {} {} not found", namespace_str, name_str),
});
wasmer_result_t::WASMER_ERROR
}
}
/// private wrapper data type used for casting
#[repr(C)]
struct WasmerImportObjectIterator(
std::iter::Peekable<Box<dyn Iterator<Item = <ImportObjectIterator as Iterator>::Item>>>,
);
/// Create an iterator over the functions in the import object.
/// Get the next import with `wasmer_import_object_iter_next`
/// Free the iterator with `wasmer_import_object_iter_destroy`
#[no_mangle]
pub unsafe extern "C" fn wasmer_import_object_iterate_functions(
import_object: *const wasmer_import_object_t,
) -> *mut wasmer_import_object_iter_t {
if import_object.is_null() {
update_last_error(CApiError {
msg: "import_object must not be null".to_owned(),
});
return std::ptr::null_mut();
}
let import_object: &ImportObject = &*(import_object as *const ImportObject);
let iter_inner = Box::new(import_object.clone_ref().into_iter().filter(|(_, _, e)| {
if let Export::Function { .. } = e {
true
} else {
false
}
})) as Box<dyn Iterator<Item = <ImportObjectIterator as Iterator>::Item>>;
let iterator = Box::new(WasmerImportObjectIterator(iter_inner.peekable()));
Box::into_raw(iterator) as *mut wasmer_import_object_iter_t
}
/// Writes the next value to `import`. `WASMER_ERROR` is returned if there
/// was an error or there's nothing left to return.
///
/// To free the memory allocated here, pass the import to `wasmer_import_object_imports_destroy`.
/// To check if the iterator is done, use `wasmer_import_object_iter_at_end`.
#[no_mangle]
pub unsafe extern "C" fn wasmer_import_object_iter_next(
import_object_iter: *mut wasmer_import_object_iter_t,
import: *mut wasmer_import_t,
) -> wasmer_result_t {
if import_object_iter.is_null() || import.is_null() {
update_last_error(CApiError {
msg: "import_object_iter and import must not be null".to_owned(),
});
return wasmer_result_t::WASMER_ERROR;
}
let iter = &mut *(import_object_iter as *mut WasmerImportObjectIterator);
let out = &mut *import;
// TODO: the copying here can be optimized away, we just need to use a different type of
// iterator internally
if let Some((namespace, name, export)) = iter.0.next() {
let ns = {
let mut n = namespace.clone();
n.shrink_to_fit();
n.into_bytes()
};
let ns_bytes = wasmer_byte_array {
bytes: ns.as_ptr(),
bytes_len: ns.len() as u32,
};
let name = {
let mut n = name.clone();
n.shrink_to_fit();
n.into_bytes()
};
let name_bytes = wasmer_byte_array {
bytes: name.as_ptr(),
bytes_len: name.len() as u32,
};
out.module_name = ns_bytes;
out.import_name = name_bytes;
std::mem::forget(ns);
std::mem::forget(name);
match export {
Export::Function { .. } => {
let func = Box::new(export.clone());
out.tag = wasmer_import_export_kind::WASM_FUNCTION;
out.value = wasmer_import_export_value {
func: Box::into_raw(func) as *mut _ as *const _,
};
}
Export::Global(global) => {
let glbl = Box::new(global.clone());
out.tag = wasmer_import_export_kind::WASM_GLOBAL;
out.value = wasmer_import_export_value {
global: Box::into_raw(glbl) as *mut _ as *const _,
};
}
Export::Memory(memory) => {
let mem = Box::new(memory.clone());
out.tag = wasmer_import_export_kind::WASM_MEMORY;
out.value = wasmer_import_export_value {
memory: Box::into_raw(mem) as *mut _ as *const _,
};
}
Export::Table(table) => {
let tbl = Box::new(table.clone());
out.tag = wasmer_import_export_kind::WASM_TABLE;
out.value = wasmer_import_export_value {
memory: Box::into_raw(tbl) as *mut _ as *const _,
};
}
}
wasmer_result_t::WASMER_OK
} else {
wasmer_result_t::WASMER_ERROR
}
}
/// Returns true if further calls to `wasmer_import_object_iter_next` will
/// not return any new data
#[no_mangle]
pub unsafe extern "C" fn wasmer_import_object_iter_at_end(
import_object_iter: *mut wasmer_import_object_iter_t,
) -> bool {
if import_object_iter.is_null() {
update_last_error(CApiError {
msg: "import_object_iter must not be null".to_owned(),
});
return true;
}
let iter = &mut *(import_object_iter as *mut WasmerImportObjectIterator);
iter.0.peek().is_none()
}
/// Frees the memory allocated by `wasmer_import_object_iterate_functions`
#[no_mangle]
pub unsafe extern "C" fn wasmer_import_object_iter_destroy(
import_object_iter: *mut wasmer_import_object_iter_t,
) {
if !import_object_iter.is_null() {
let _ = Box::from_raw(import_object_iter as *mut WasmerImportObjectIterator);
}
}
/// Frees the memory allocated in `wasmer_import_object_iter_next`
///
/// This function does not free the memory in `wasmer_import_object_t`;
/// it only frees memory allocated while querying a `wasmer_import_object_t`.
#[no_mangle]
pub unsafe extern "C" fn wasmer_import_object_imports_destroy(
imports: *mut wasmer_import_t,
imports_len: u32,
) {
if imports.is_null() {
return;
}
let imports: &[wasmer_import_t] = &*slice::from_raw_parts_mut(imports, imports_len as usize);
for import in imports {
let _namespace: Vec<u8> = Vec::from_raw_parts(
import.module_name.bytes as *mut u8,
import.module_name.bytes_len as usize,
import.module_name.bytes_len as usize,
);
let _name: Vec<u8> = Vec::from_raw_parts(
import.import_name.bytes as *mut u8,
import.import_name.bytes_len as usize,
import.import_name.bytes_len as usize,
);
match import.tag {
wasmer_import_export_kind::WASM_FUNCTION => {
let _: Box<Export> = Box::from_raw(import.value.func as *mut _);
}
wasmer_import_export_kind::WASM_GLOBAL => {
let _: Box<Global> = Box::from_raw(import.value.global as *mut _);
}
wasmer_import_export_kind::WASM_MEMORY => {
let _: Box<Memory> = Box::from_raw(import.value.memory as *mut _);
}
wasmer_import_export_kind::WASM_TABLE => {
let _: Box<Table> = Box::from_raw(import.value.table as *mut _);
}
}
}
}
/// Extends an existing import object with new imports /// Extends an existing import object with new imports
#[allow(clippy::cast_ptr_alignment)] #[allow(clippy::cast_ptr_alignment)]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn wasmer_import_object_extend( pub unsafe extern "C" fn wasmer_import_object_extend(
import_object: *mut wasmer_import_object_t, import_object: *mut wasmer_import_object_t,
imports: *mut wasmer_import_t, imports: *const wasmer_import_t,
imports_len: c_uint, imports_len: c_uint,
) -> wasmer_result_t { ) -> wasmer_result_t {
let import_object: &mut ImportObject = &mut *(import_object as *mut ImportObject); let import_object: &mut ImportObject = &mut *(import_object as *mut ImportObject);

View File

@ -0,0 +1,101 @@
use super::*;
use crate::get_slice_checked;
use std::path::PathBuf;
/// Opens a directory that's visible to the WASI module as `alias` but
/// is backed by the host file at `host_file_path`
#[repr(C)]
pub struct wasmer_wasi_map_dir_entry_t {
/// What the WASI module will see in its virtual root
pub alias: wasmer_byte_array,
/// The backing file that the WASI module will interact with via the alias
pub host_file_path: wasmer_byte_array,
}
impl wasmer_wasi_map_dir_entry_t {
/// Converts the data into owned, Rust types
pub unsafe fn as_tuple(&self) -> Result<(String, PathBuf), std::str::Utf8Error> {
let alias = self.alias.as_str()?.to_owned();
let host_path = std::path::PathBuf::from(self.host_file_path.as_str()?);
Ok((alias, host_path))
}
}
/// Creates a WASI import object.
///
/// This function treats null pointers as empty collections.
/// For example, passing null for a string in `args`, will lead to a zero
/// length argument in that position.
#[no_mangle]
pub unsafe extern "C" fn wasmer_wasi_generate_import_object(
args: *const wasmer_byte_array,
args_len: c_uint,
envs: *const wasmer_byte_array,
envs_len: c_uint,
preopened_files: *const wasmer_byte_array,
preopened_files_len: c_uint,
mapped_dirs: *const wasmer_wasi_map_dir_entry_t,
mapped_dirs_len: c_uint,
) -> *mut wasmer_import_object_t {
let arg_list = get_slice_checked(args, args_len as usize);
let env_list = get_slice_checked(envs, envs_len as usize);
let preopened_file_list = get_slice_checked(preopened_files, preopened_files_len as usize);
let mapped_dir_list = get_slice_checked(mapped_dirs, mapped_dirs_len as usize);
wasmer_wasi_generate_import_object_inner(
arg_list,
env_list,
preopened_file_list,
mapped_dir_list,
)
.unwrap_or(std::ptr::null_mut())
}
/// Inner function that wraps error handling
fn wasmer_wasi_generate_import_object_inner(
arg_list: &[wasmer_byte_array],
env_list: &[wasmer_byte_array],
preopened_file_list: &[wasmer_byte_array],
mapped_dir_list: &[wasmer_wasi_map_dir_entry_t],
) -> Result<*mut wasmer_import_object_t, std::str::Utf8Error> {
let arg_vec = arg_list.iter().map(|arg| unsafe { arg.as_vec() }).collect();
let env_vec = env_list
.iter()
.map(|env_var| unsafe { env_var.as_vec() })
.collect();
let po_file_vec = preopened_file_list
.iter()
.map(|po_file| Ok(unsafe { PathBuf::from(po_file.as_str()?) }.to_owned()))
.collect::<Result<Vec<_>, _>>()?;
let mapped_dir_vec = mapped_dir_list
.iter()
.map(|entry| unsafe { entry.as_tuple() })
.collect::<Result<Vec<_>, _>>()?;
let import_object = Box::new(wasmer_wasi::generate_import_object(
arg_vec,
env_vec,
po_file_vec,
mapped_dir_vec,
));
Ok(Box::into_raw(import_object) as *mut wasmer_import_object_t)
}
/// Convenience function that creates a WASI import object with no arguments,
/// environment variables, preopened files, or mapped directories.
///
/// This function is the same as calling [`wasmer_wasi_generate_import_object`] with all
/// empty values.
#[no_mangle]
pub unsafe extern "C" fn wasmer_wasi_generate_default_import_object() -> *mut wasmer_import_object_t
{
let import_object = Box::new(wasmer_wasi::generate_import_object(
vec![],
vec![],
vec![],
vec![],
));
Box::into_raw(import_object) as *mut wasmer_import_object_t
}

View File

@ -75,6 +75,7 @@ pub unsafe extern "C" fn wasmer_instantiate(
let namespace = namespaces.entry(module_name).or_insert_with(Namespace::new); let namespace = namespaces.entry(module_name).or_insert_with(Namespace::new);
// TODO check that tag is actually in bounds here
let export = match import.tag { let export = match import.tag {
wasmer_import_export_kind::WASM_MEMORY => { wasmer_import_export_kind::WASM_MEMORY => {
let mem = import.value.memory as *mut Memory; let mem = import.value.memory as *mut Memory;

View File

@ -129,3 +129,34 @@ pub struct wasmer_byte_array {
pub bytes: *const u8, pub bytes: *const u8,
pub bytes_len: u32, pub bytes_len: u32,
} }
impl wasmer_byte_array {
/// Get the data as a slice
pub unsafe fn as_slice<'a>(&self) -> &'a [u8] {
get_slice_checked(self.bytes, self.bytes_len as usize)
}
/// Copy the data into an owned Vec
pub unsafe fn as_vec(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(self.bytes_len as usize);
out.extend_from_slice(self.as_slice());
out
}
/// Read the data as a &str, returns an error if the string is not valid UTF8
pub unsafe fn as_str<'a>(&self) -> Result<&'a str, std::str::Utf8Error> {
std::str::from_utf8(self.as_slice())
}
}
/// Gets a slice from a pointer and a length, returning an empty slice if the
/// pointer is null
#[inline]
pub(crate) unsafe fn get_slice_checked<'a, T>(ptr: *const T, len: usize) -> &'a [T] {
if ptr.is_null() {
&[]
} else {
std::slice::from_raw_parts(ptr, len)
}
}

View File

@ -1,6 +1,6 @@
//! Create, read, write, grow, destroy memory of an instance. //! Create, read, write, grow, destroy memory of an instance.
use crate::{error::update_last_error, wasmer_limits_t, wasmer_result_t}; use crate::{error::update_last_error, error::CApiError, wasmer_limits_t, wasmer_result_t};
use std::cell::Cell; use std::cell::Cell;
use wasmer_runtime::Memory; use wasmer_runtime::Memory;
use wasmer_runtime_core::{ use wasmer_runtime_core::{
@ -31,12 +31,17 @@ pub unsafe extern "C" fn wasmer_memory_new(
} else { } else {
None None
}; };
let desc = MemoryDescriptor { let desc = MemoryDescriptor::new(Pages(limits.min), max, false);
minimum: Pages(limits.min), let new_desc = match desc {
maximum: max, Ok(desc) => desc,
shared: false, Err(error) => {
update_last_error(CApiError {
msg: error.to_string(),
});
return wasmer_result_t::WASMER_ERROR;
}
}; };
let result = Memory::new(desc); let result = Memory::new(new_desc);
let new_memory = match result { let new_memory = match result {
Ok(memory) => memory, Ok(memory) => memory,
Err(error) => { Err(error) => {

View File

@ -25,3 +25,5 @@ test-tables
test-validate test-validate
test-context test-context
test-module-import-instantiate test-module-import-instantiate
test-wasi-import-object

View File

@ -6,6 +6,8 @@ add_executable(test-exports test-exports.c)
add_executable(test-globals test-globals.c) add_executable(test-globals test-globals.c)
add_executable(test-import-function test-import-function.c) add_executable(test-import-function test-import-function.c)
add_executable(test-imports test-imports.c) add_executable(test-imports test-imports.c)
add_executable(test-import-object test-import-object.c)
add_executable(test-wasi-import-object test-wasi-import-object.c)
add_executable(test-instantiate test-instantiate.c) add_executable(test-instantiate test-instantiate.c)
add_executable(test-memory test-memory.c) add_executable(test-memory test-memory.c)
add_executable(test-module test-module.c) add_executable(test-module test-module.c)
@ -58,6 +60,14 @@ target_link_libraries(test-imports general ${WASMER_LIB})
target_compile_options(test-imports PRIVATE ${COMPILER_OPTIONS}) target_compile_options(test-imports PRIVATE ${COMPILER_OPTIONS})
add_test(test-imports test-imports) add_test(test-imports test-imports)
target_link_libraries(test-import-object general ${WASMER_LIB})
target_compile_options(test-import-object PRIVATE ${COMPILER_OPTIONS})
add_test(test-import-object test-import-object)
target_link_libraries(test-wasi-import-object general ${WASMER_LIB})
target_compile_options(test-wasi-import-object PRIVATE ${COMPILER_OPTIONS})
add_test(test-wasi-import-object test-wasi-import-object)
target_link_libraries(test-instantiate general ${WASMER_LIB}) target_link_libraries(test-instantiate general ${WASMER_LIB})
target_compile_options(test-instantiate PRIVATE ${COMPILER_OPTIONS}) target_compile_options(test-instantiate PRIVATE ${COMPILER_OPTIONS})
add_test(test-instantiate test-instantiate) add_test(test-instantiate test-instantiate)

View File

@ -0,0 +1,3 @@
These are used in tests in the parent directory.
To keep the generated wasm small, use `wasm-opt` and `wasm-strip` from wabt-tools (can be installed via wapm). Addtionally, consider passing the `-C opt-level=z` flag to `rustc` to optimize for size.

View File

@ -0,0 +1,31 @@
extern "C" {
fn host_print(ptr: u32, len: u32);
}
fn main() {
let args = std::env::args().collect::<Vec<String>>();
println!("Found {} args on program {}", args.len(), args[0]);
let env_vars = std::env::vars()
.map(|(arg, val)| format!("{}={}", arg, val))
.collect::<Vec<String>>();
let env_var_list = env_vars.join(", ");
println!("Found {} env vars: {}", env_vars.len(), env_var_list);
let dirs_in_root = std::fs::read_dir("/")
.unwrap()
.map(|e| e.map(|inner| format!("{:?}", inner)))
.collect::<Result<Vec<String>, _>>()
.unwrap();
println!(
"Found {} pre opened dirs: {}",
dirs_in_root.len(),
dirs_in_root.join(", ")
);
const HOST_STR: &str = "This string came from a WASI module";
unsafe { host_print(HOST_STR.as_ptr() as u32, HOST_STR.len() as u32) };
}

Binary file not shown.

View File

@ -1,3 +1,4 @@
#include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include "../wasmer.h" #include "../wasmer.h"
#include <assert.h> #include <assert.h>
@ -242,7 +243,7 @@ int main()
wasmer_result_t call_result = wasmer_export_func_call(exported_function, inputs, inputs_arity, outputs, outputs_arity); wasmer_result_t call_result = wasmer_export_func_call(exported_function, inputs, inputs_arity, outputs, outputs_arity);
printf("Result: %lld\n", outputs[0].value.I64); printf("Result: %" PRId64 "\n", outputs[0].value.I64);
assert(outputs[0].value.I64 == 7); assert(outputs[0].value.I64 == 7);
assert(call_result == WASMER_OK); assert(call_result == WASMER_OK);

Binary file not shown.

View File

@ -0,0 +1,172 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
bool static print_str_called = false;
// Host function that will be imported into the Web Assembly Instance
void print_str(const wasmer_instance_context_t *ctx, int32_t ptr, int32_t len)
{
print_str_called = true;
const wasmer_memory_t *memory = wasmer_instance_context_memory(ctx, 0);
uint32_t mem_len = wasmer_memory_length(memory);
uint8_t *mem_bytes = wasmer_memory_data(memory);
printf("%.*s", len, mem_bytes + ptr);
}
// Use the last_error API to retrieve error messages
void print_wasmer_error()
{
int error_len = wasmer_last_error_length();
printf("Error len: `%d`\n", error_len);
char *error_str = malloc(error_len);
wasmer_last_error_message(error_str, error_len);
printf("Error str: `%s`\n", error_str);
}
int main()
{
// Create a new func to hold the parameter and signature
// of our `print_str` host function
wasmer_value_tag params_sig[] = {WASM_I32, WASM_I32};
wasmer_value_tag returns_sig[] = {};
wasmer_import_func_t *func = wasmer_import_func_new((void (*)(void *)) print_str, params_sig, 2, returns_sig, 0);
// Create module name for our imports
// represented in bytes for UTF-8 compatability
const char *module_name = "env";
wasmer_byte_array module_name_bytes;
module_name_bytes.bytes = (const uint8_t *) module_name;
module_name_bytes.bytes_len = strlen(module_name);
// Define a function import
const char *import_name = "_print_str";
wasmer_byte_array import_name_bytes;
import_name_bytes.bytes = (const uint8_t *) import_name;
import_name_bytes.bytes_len = strlen(import_name);
wasmer_import_t func_import;
func_import.module_name = module_name_bytes;
func_import.import_name = import_name_bytes;
func_import.tag = WASM_FUNCTION;
func_import.value.func = func;
// Define a memory import
const char *import_memory_name = "memory";
wasmer_byte_array import_memory_name_bytes;
import_memory_name_bytes.bytes = (const uint8_t *) import_memory_name;
import_memory_name_bytes.bytes_len = strlen(import_memory_name);
wasmer_import_t memory_import;
memory_import.module_name = module_name_bytes;
memory_import.import_name = import_memory_name_bytes;
memory_import.tag = WASM_MEMORY;
wasmer_memory_t *memory = NULL;
wasmer_limits_t descriptor;
descriptor.min = 256;
wasmer_limit_option_t max;
max.has_some = true;
max.some = 256;
descriptor.max = max;
wasmer_result_t memory_result = wasmer_memory_new(&memory, descriptor);
if (memory_result != WASMER_OK)
{
print_wasmer_error();
}
memory_import.value.memory = memory;
// Define a global import
const char *import_global_name = "__memory_base";
wasmer_byte_array import_global_name_bytes;
import_global_name_bytes.bytes = (const uint8_t *) import_global_name;
import_global_name_bytes.bytes_len = strlen(import_global_name);
wasmer_import_t global_import;
global_import.module_name = module_name_bytes;
global_import.import_name = import_global_name_bytes;
global_import.tag = WASM_GLOBAL;
wasmer_value_t val;
val.tag = WASM_I32;
val.value.I32 = 1024;
wasmer_global_t *global = wasmer_global_new(val, false);
global_import.value.global = global;
// Define a table import
const char *import_table_name = "table";
wasmer_byte_array import_table_name_bytes;
import_table_name_bytes.bytes = (const uint8_t *) import_table_name;
import_table_name_bytes.bytes_len = strlen(import_table_name);
wasmer_import_t table_import;
table_import.module_name = module_name_bytes;
table_import.import_name = import_table_name_bytes;
table_import.tag = WASM_TABLE;
wasmer_table_t *table = NULL;
wasmer_limits_t table_descriptor;
table_descriptor.min = 256;
wasmer_limit_option_t table_max;
table_max.has_some = true;
table_max.some = 256;
table_descriptor.max = table_max;
wasmer_result_t table_result = wasmer_table_new(&table, table_descriptor);
if (table_result != WASMER_OK)
{
print_wasmer_error();
}
table_import.value.table = table;
// Define an empty import object
wasmer_import_object_t *import_object = wasmer_import_object_new();
// Create our imports
wasmer_import_t imports[] = {func_import, global_import, memory_import, table_import};
int imports_len = sizeof(imports) / sizeof(imports[0]);
// Add our imports to the import object
wasmer_import_object_extend(import_object, imports, imports_len);
// Read the wasm file bytes
FILE *file = fopen("assets/hello_wasm.wasm", "r");
fseek(file, 0, SEEK_END);
long len = ftell(file);
uint8_t *bytes = malloc(len);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, len, file);
fclose(file);
wasmer_module_t *module = NULL;
// Compile the WebAssembly module
wasmer_result_t compile_result = wasmer_compile(&module, bytes, len);
printf("Compile result: %d\n", compile_result);
if (compile_result != WASMER_OK)
{
print_wasmer_error();
}
assert(compile_result == WASMER_OK);
// Instantiatoe the module with our import_object
wasmer_instance_t *instance = NULL;
wasmer_result_t instantiate_result = wasmer_module_import_instantiate(&instance, module, import_object);
printf("Instantiate result: %d\n", instantiate_result);
if (instantiate_result != WASMER_OK)
{
print_wasmer_error();
}
assert(instantiate_result == WASMER_OK);
// Call the exported "hello_wasm" function of our instance
wasmer_value_t params[] = {};
wasmer_value_t result_one;
wasmer_value_t results[] = {result_one};
wasmer_result_t call_result = wasmer_instance_call(instance, "_hello_wasm", params, 0, results, 1);
printf("Call result: %d\n", call_result);
assert(call_result == WASMER_OK);
assert(print_str_called);
// Use *_destroy methods to cleanup as specified in the header documentation
wasmer_import_func_destroy(func);
wasmer_global_destroy(global);
wasmer_memory_destroy(memory);
wasmer_table_destroy(table);
wasmer_instance_destroy(instance);
wasmer_import_object_destroy(import_object);
wasmer_module_destroy(module);
return 0;
}

Binary file not shown.

View File

@ -0,0 +1,250 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
static bool host_print_called = false;
// Host function that will be imported into the Web Assembly Instance
void host_print(const wasmer_instance_context_t *ctx, int32_t ptr, int32_t len)
{
host_print_called = true;
const wasmer_memory_t *memory = wasmer_instance_context_memory(ctx, 0);
uint32_t mem_len = wasmer_memory_length(memory);
uint8_t *mem_bytes = wasmer_memory_data(memory);
printf("%.*s", len, mem_bytes + ptr);
}
// Use the last_error API to retrieve error messages
void print_wasmer_error()
{
int error_len = wasmer_last_error_length();
printf("Error len: `%d`\n", error_len);
char *error_str = malloc(error_len);
wasmer_last_error_message(error_str, error_len);
printf("Error str: `%s`\n", error_str);
}
// helper function to print byte array to stdout
void print_byte_array(wasmer_byte_array *arr) {
for (int i = 0; i < arr->bytes_len; ++i) {
putchar(arr->bytes[i]);
}
}
int main()
{
// Create a new func to hold the parameter and signature
// of our `host_print` host function
wasmer_value_tag params_sig[] = {WASM_I32, WASM_I32};
wasmer_value_tag returns_sig[] = {};
wasmer_import_func_t *func = wasmer_import_func_new((void (*)(void *)) host_print, params_sig, 2, returns_sig, 0);
// Create module name for our imports
// represented in bytes for UTF-8 compatability
const char *module_name = "env";
wasmer_byte_array module_name_bytes;
module_name_bytes.bytes = (const uint8_t *) module_name;
module_name_bytes.bytes_len = strlen(module_name);
// Define a function import
const char *import_name = "host_print";
wasmer_byte_array import_name_bytes;
import_name_bytes.bytes = (const uint8_t *) import_name;
import_name_bytes.bytes_len = strlen(import_name);
wasmer_import_t func_import;
func_import.module_name = module_name_bytes;
func_import.import_name = import_name_bytes;
func_import.tag = WASM_FUNCTION;
func_import.value.func = func;
// Define a memory import
const char *import_memory_name = "memory";
wasmer_byte_array import_memory_name_bytes;
import_memory_name_bytes.bytes = (const uint8_t *) import_memory_name;
import_memory_name_bytes.bytes_len = strlen(import_memory_name);
wasmer_import_t memory_import;
memory_import.module_name = module_name_bytes;
memory_import.import_name = import_memory_name_bytes;
memory_import.tag = WASM_MEMORY;
wasmer_memory_t *memory = NULL;
wasmer_limits_t descriptor;
descriptor.min = 256;
wasmer_limit_option_t max;
max.has_some = true;
max.some = 256;
descriptor.max = max;
wasmer_result_t memory_result = wasmer_memory_new(&memory, descriptor);
if (memory_result != WASMER_OK)
{
print_wasmer_error();
}
memory_import.value.memory = memory;
// Define a global import
const char *import_global_name = "__memory_base";
wasmer_byte_array import_global_name_bytes;
import_global_name_bytes.bytes = (const uint8_t *) import_global_name;
import_global_name_bytes.bytes_len = strlen(import_global_name);
wasmer_import_t global_import;
global_import.module_name = module_name_bytes;
global_import.import_name = import_global_name_bytes;
global_import.tag = WASM_GLOBAL;
wasmer_value_t val;
val.tag = WASM_I32;
val.value.I32 = 1024;
wasmer_global_t *global = wasmer_global_new(val, false);
global_import.value.global = global;
// Define a table import
const char *import_table_name = "table";
wasmer_byte_array import_table_name_bytes;
import_table_name_bytes.bytes = (const uint8_t *) import_table_name;
import_table_name_bytes.bytes_len = strlen(import_table_name);
wasmer_import_t table_import;
table_import.module_name = module_name_bytes;
table_import.import_name = import_table_name_bytes;
table_import.tag = WASM_TABLE;
wasmer_table_t *table = NULL;
wasmer_limits_t table_descriptor;
table_descriptor.min = 256;
wasmer_limit_option_t table_max;
table_max.has_some = true;
table_max.some = 256;
table_descriptor.max = table_max;
wasmer_result_t table_result = wasmer_table_new(&table, table_descriptor);
if (table_result != WASMER_OK)
{
print_wasmer_error();
}
table_import.value.table = table;
// Create arbitrary arguments for our program
// Set up data for our WASI import object
//
// Environment variables and program arguments are processed by the WASI
// program. They will not have any effects unless the program includes
// logic to process them.
const char *wasi_prog_name = "wasi_test_program";
const char *wasi_first_arg = "--help";
wasmer_byte_array args[] = {
{ .bytes = (const uint8_t *) wasi_prog_name,
.bytes_len = strlen(wasi_prog_name) },
{ .bytes = (const uint8_t *) wasi_first_arg,
.bytes_len = strlen(wasi_first_arg) }
};
int wasi_argc = sizeof(args) / sizeof(args[0]);
// Create arbitrary environment variables for our program;
const char *wasi_color_env = "COLOR=TRUE";
const char *wasi_app_should_log = "APP_SHOULD_LOG=FALSE";
wasmer_byte_array envs[] = {
{ .bytes = (const uint8_t *) wasi_color_env,
.bytes_len = strlen(wasi_color_env) },
{ .bytes = (const uint8_t *) wasi_app_should_log,
.bytes_len = strlen(wasi_app_should_log) }
};
int wasi_env_len = sizeof(args) / sizeof(args[0]);
// Open the host's current directory under a different name.
// WARNING: this gives the WASI module limited access to your host's file system,
// use caution when granting these permissions to untrusted Wasm modules.
const char *wasi_map_dir_alias = "the_host_current_dir";
const char *wasi_map_dir_host_path = ".";
wasmer_wasi_map_dir_entry_t mapped_dirs[] = {
{ .alias =
{ .bytes = (const uint8_t *) wasi_map_dir_alias,
.bytes_len = strlen(wasi_map_dir_alias) },
.host_file_path =
{ .bytes = (const uint8_t *) wasi_map_dir_host_path,
.bytes_len = strlen(wasi_map_dir_host_path) } }
};
int mapped_dir_len = sizeof(mapped_dirs) / sizeof(mapped_dirs[0]);
// Create the WASI import object
wasmer_import_object_t *import_object =
wasmer_wasi_generate_import_object(args, wasi_argc,
envs, wasi_env_len,
NULL, 0,
mapped_dirs, mapped_dir_len);
// Create our imports
wasmer_import_t imports[] = {func_import, global_import, memory_import, table_import};
int imports_len = sizeof(imports) / sizeof(imports[0]);
// Add our imports to the import object
wasmer_import_object_extend(import_object, imports, imports_len);
// Read the wasm file bytes
FILE *file = fopen("assets/extended_wasi.wasm", "r");
assert(file);
fseek(file, 0, SEEK_END);
long len = ftell(file);
uint8_t *bytes = malloc(len);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, len, file);
fclose(file);
wasmer_module_t *module = NULL;
// Compile the WebAssembly module
wasmer_result_t compile_result = wasmer_compile(&module, bytes, len);
printf("Compile result: %d\n", compile_result);
if (compile_result != WASMER_OK)
{
print_wasmer_error();
}
assert(compile_result == WASMER_OK);
// Instantiatoe the module with our import_object
wasmer_instance_t *instance = NULL;
wasmer_result_t instantiate_result = wasmer_module_import_instantiate(&instance, module, import_object);
printf("Instantiate result: %d\n", instantiate_result);
if (instantiate_result != WASMER_OK)
{
print_wasmer_error();
}
assert(instantiate_result == WASMER_OK);
// Call the exported "hello_wasm" function of our instance
wasmer_value_t params[] = {};
wasmer_value_t result_one;
wasmer_value_t results[] = {result_one};
// _start runs before main for WASI programs
wasmer_result_t call_result = wasmer_instance_call(instance, "_start", params, 0, results, 1);
printf("Call result: %d\n", call_result);
assert(call_result == WASMER_OK);
assert(host_print_called);
wasmer_import_object_iter_t *func_iter = wasmer_import_object_iterate_functions(import_object);
puts("Functions in import object:");
while ( !wasmer_import_object_iter_at_end(func_iter) ) {
wasmer_import_t import;
wasmer_result_t result = wasmer_import_object_iter_next(func_iter, &import);
assert(result == WASMER_OK);
print_byte_array(&import.module_name);
putchar(' ');
print_byte_array(&import.import_name);
putchar('\n');
assert(import.tag == WASM_FUNCTION);
assert(import.value.func);
wasmer_import_object_imports_destroy(&import, 1);
}
wasmer_import_object_iter_destroy(func_iter);
// Use *_destroy methods to cleanup as specified in the header documentation
wasmer_import_func_destroy(func);
wasmer_global_destroy(global);
wasmer_memory_destroy(memory);
wasmer_table_destroy(table);
wasmer_instance_destroy(instance);
wasmer_import_object_destroy(import_object);
wasmer_module_destroy(module);
return 0;
}

View File

@ -10,10 +10,10 @@
* List of export/import kinds. * List of export/import kinds.
*/ */
enum wasmer_import_export_kind { enum wasmer_import_export_kind {
WASM_FUNCTION, WASM_FUNCTION = 0,
WASM_GLOBAL, WASM_GLOBAL = 1,
WASM_MEMORY, WASM_MEMORY = 2,
WASM_TABLE, WASM_TABLE = 3,
}; };
typedef uint32_t wasmer_import_export_kind; typedef uint32_t wasmer_import_export_kind;
@ -138,6 +138,10 @@ typedef struct {
typedef struct { typedef struct {
} wasmer_import_object_iter_t;
typedef struct {
} wasmer_instance_t; } wasmer_instance_t;
typedef struct { typedef struct {
@ -170,6 +174,21 @@ typedef struct {
} wasmer_trampoline_buffer_t; } wasmer_trampoline_buffer_t;
/**
* Opens a directory that's visible to the WASI module as `alias` but
* is backed by the host file at `host_file_path`
*/
typedef struct {
/**
* What the WASI module will see in its virtual root
*/
wasmer_byte_array alias;
/**
* The backing file that the WASI module will interact with via the alias
*/
wasmer_byte_array host_file_path;
} wasmer_wasi_map_dir_entry_t;
/** /**
* Creates a new Module from the given wasm bytes. * Creates a new Module from the given wasm bytes.
* *
@ -451,9 +470,60 @@ void wasmer_import_object_destroy(wasmer_import_object_t *import_object);
* Extends an existing import object with new imports * Extends an existing import object with new imports
*/ */
wasmer_result_t wasmer_import_object_extend(wasmer_import_object_t *import_object, wasmer_result_t wasmer_import_object_extend(wasmer_import_object_t *import_object,
wasmer_import_t *imports, const wasmer_import_t *imports,
unsigned int imports_len); unsigned int imports_len);
/**
* Gets an entry from an ImportObject at the name and namespace.
* Stores `name`, `namespace`, and `import_export_value` in `import`.
* Thus these must remain valid for the lifetime of `import`.
*
* The caller owns all data involved.
* `import_export_value` will be written to based on `tag`.
*/
wasmer_result_t wasmer_import_object_get_import(const wasmer_import_object_t *import_object,
wasmer_byte_array namespace_,
wasmer_byte_array name,
wasmer_import_t *import,
wasmer_import_export_value *import_export_value,
uint32_t tag);
/**
* Frees the memory allocated in `wasmer_import_object_iter_next`
*
* This function does not free the memory in `wasmer_import_object_t`;
* it only frees memory allocated while querying a `wasmer_import_object_t`.
*/
void wasmer_import_object_imports_destroy(wasmer_import_t *imports, uint32_t imports_len);
/**
* Returns true if further calls to `wasmer_import_object_iter_next` will
* not return any new data
*/
bool wasmer_import_object_iter_at_end(wasmer_import_object_iter_t *import_object_iter);
/**
* Frees the memory allocated by `wasmer_import_object_iterate_functions`
*/
void wasmer_import_object_iter_destroy(wasmer_import_object_iter_t *import_object_iter);
/**
* Writes the next value to `import`. `WASMER_ERROR` is returned if there
* was an error or there's nothing left to return.
*
* To free the memory allocated here, pass the import to `wasmer_import_object_imports_destroy`.
* To check if the iterator is done, use `wasmer_import_object_iter_at_end`.
*/
wasmer_result_t wasmer_import_object_iter_next(wasmer_import_object_iter_t *import_object_iter,
wasmer_import_t *import);
/**
* Create an iterator over the functions in the import object.
* Get the next import with `wasmer_import_object_iter_next`
* Free the iterator with `wasmer_import_object_iter_destroy`
*/
wasmer_import_object_iter_t *wasmer_import_object_iterate_functions(const wasmer_import_object_t *import_object);
/** /**
* Creates a new empty import object. * Creates a new empty import object.
* See also `wasmer_import_object_append` * See also `wasmer_import_object_append`
@ -756,4 +826,29 @@ void *wasmer_trampoline_get_context(void);
*/ */
bool wasmer_validate(const uint8_t *wasm_bytes, uint32_t wasm_bytes_len); bool wasmer_validate(const uint8_t *wasm_bytes, uint32_t wasm_bytes_len);
/**
* Convenience function that creates a WASI import object with no arguments,
* environment variables, preopened files, or mapped directories.
*
* This function is the same as calling [`wasmer_wasi_generate_import_object`] with all
* empty values.
*/
wasmer_import_object_t *wasmer_wasi_generate_default_import_object(void);
/**
* Creates a WASI import object.
*
* This function treats null pointers as empty collections.
* For example, passing null for a string in `args`, will lead to a zero
* length argument in that position.
*/
wasmer_import_object_t *wasmer_wasi_generate_import_object(const wasmer_byte_array *args,
unsigned int args_len,
const wasmer_byte_array *envs,
unsigned int envs_len,
const wasmer_byte_array *preopened_files,
unsigned int preopened_files_len,
const wasmer_wasi_map_dir_entry_t *mapped_dirs,
unsigned int mapped_dirs_len);
#endif /* WASMER_H */ #endif /* WASMER_H */

View File

@ -8,10 +8,10 @@
/// List of export/import kinds. /// List of export/import kinds.
enum class wasmer_import_export_kind : uint32_t { enum class wasmer_import_export_kind : uint32_t {
WASM_FUNCTION, WASM_FUNCTION = 0,
WASM_GLOBAL, WASM_GLOBAL = 1,
WASM_MEMORY, WASM_MEMORY = 2,
WASM_TABLE, WASM_TABLE = 3,
}; };
enum class wasmer_result_t { enum class wasmer_result_t {
@ -120,6 +120,10 @@ struct wasmer_import_t {
wasmer_import_export_value value; wasmer_import_export_value value;
}; };
struct wasmer_import_object_iter_t {
};
struct wasmer_instance_t { struct wasmer_instance_t {
}; };
@ -154,6 +158,15 @@ struct wasmer_trampoline_buffer_t {
}; };
/// Opens a directory that's visible to the WASI module as `alias` but
/// is backed by the host file at `host_file_path`
struct wasmer_wasi_map_dir_entry_t {
/// What the WASI module will see in its virtual root
wasmer_byte_array alias;
/// The backing file that the WASI module will interact with via the alias
wasmer_byte_array host_file_path;
};
extern "C" { extern "C" {
/// Creates a new Module from the given wasm bytes. /// Creates a new Module from the given wasm bytes.
@ -359,9 +372,48 @@ void wasmer_import_object_destroy(wasmer_import_object_t *import_object);
/// Extends an existing import object with new imports /// Extends an existing import object with new imports
wasmer_result_t wasmer_import_object_extend(wasmer_import_object_t *import_object, wasmer_result_t wasmer_import_object_extend(wasmer_import_object_t *import_object,
wasmer_import_t *imports, const wasmer_import_t *imports,
unsigned int imports_len); unsigned int imports_len);
/// Gets an entry from an ImportObject at the name and namespace.
/// Stores `name`, `namespace`, and `import_export_value` in `import`.
/// Thus these must remain valid for the lifetime of `import`.
///
/// The caller owns all data involved.
/// `import_export_value` will be written to based on `tag`.
wasmer_result_t wasmer_import_object_get_import(const wasmer_import_object_t *import_object,
wasmer_byte_array namespace_,
wasmer_byte_array name,
wasmer_import_t *import,
wasmer_import_export_value *import_export_value,
uint32_t tag);
/// Frees the memory allocated in `wasmer_import_object_iter_next`
///
/// This function does not free the memory in `wasmer_import_object_t`;
/// it only frees memory allocated while querying a `wasmer_import_object_t`.
void wasmer_import_object_imports_destroy(wasmer_import_t *imports, uint32_t imports_len);
/// Returns true if further calls to `wasmer_import_object_iter_next` will
/// not return any new data
bool wasmer_import_object_iter_at_end(wasmer_import_object_iter_t *import_object_iter);
/// Frees the memory allocated by `wasmer_import_object_iterate_functions`
void wasmer_import_object_iter_destroy(wasmer_import_object_iter_t *import_object_iter);
/// Writes the next value to `import`. `WASMER_ERROR` is returned if there
/// was an error or there's nothing left to return.
///
/// To free the memory allocated here, pass the import to `wasmer_import_object_imports_destroy`.
/// To check if the iterator is done, use `wasmer_import_object_iter_at_end`.
wasmer_result_t wasmer_import_object_iter_next(wasmer_import_object_iter_t *import_object_iter,
wasmer_import_t *import);
/// Create an iterator over the functions in the import object.
/// Get the next import with `wasmer_import_object_iter_next`
/// Free the iterator with `wasmer_import_object_iter_destroy`
wasmer_import_object_iter_t *wasmer_import_object_iterate_functions(const wasmer_import_object_t *import_object);
/// Creates a new empty import object. /// Creates a new empty import object.
/// See also `wasmer_import_object_append` /// See also `wasmer_import_object_append`
wasmer_import_object_t *wasmer_import_object_new(); wasmer_import_object_t *wasmer_import_object_new();
@ -590,6 +642,27 @@ void *wasmer_trampoline_get_context();
/// Returns true for valid wasm bytes and false for invalid bytes /// Returns true for valid wasm bytes and false for invalid bytes
bool wasmer_validate(const uint8_t *wasm_bytes, uint32_t wasm_bytes_len); bool wasmer_validate(const uint8_t *wasm_bytes, uint32_t wasm_bytes_len);
/// Convenience function that creates a WASI import object with no arguments,
/// environment variables, preopened files, or mapped directories.
///
/// This function is the same as calling [`wasmer_wasi_generate_import_object`] with all
/// empty values.
wasmer_import_object_t *wasmer_wasi_generate_default_import_object();
/// Creates a WASI import object.
///
/// This function treats null pointers as empty collections.
/// For example, passing null for a string in `args`, will lead to a zero
/// length argument in that position.
wasmer_import_object_t *wasmer_wasi_generate_import_object(const wasmer_byte_array *args,
unsigned int args_len,
const wasmer_byte_array *envs,
unsigned int envs_len,
const wasmer_byte_array *preopened_files,
unsigned int preopened_files_len,
const wasmer_wasi_map_dir_entry_t *mapped_dirs,
unsigned int mapped_dirs_len);
} // extern "C" } // extern "C"
#endif // WASMER_H #endif // WASMER_H

View File

@ -0,0 +1,21 @@
[package]
name = "wasmer-runtime-core-tests"
version = "0.9.0"
description = "Tests for the Wasmer runtime core crate"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
edition = "2018"
publish = false
[dependencies]
wabt = "0.9.1"
wasmer-runtime-core = { path = "../runtime-core", version = "0.9" }
wasmer-clif-backend = { path = "../clif-backend", version = "0.9", optional = true }
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.9", optional = true }
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.9", optional = true }
[features]
default = ["backend-cranelift"]
backend-cranelift = ["wasmer-clif-backend"]
backend-singlepass = ["wasmer-singlepass-backend"]
backend-llvm = ["wasmer-llvm-backend"]

View File

@ -0,0 +1,21 @@
pub use wabt::wat2wasm;
use wasmer_runtime_core::backend::Compiler;
#[cfg(feature = "backend-cranelift")]
pub fn get_compiler() -> impl Compiler {
use wasmer_clif_backend::CraneliftCompiler;
CraneliftCompiler::new()
}
#[cfg(feature = "backend-singlepass")]
pub fn get_compiler() -> impl Compiler {
use wasmer_singlepass_backend::SinglePassCompiler;
SinglePassCompiler::new()
}
#[cfg(feature = "backend-llvm")]
pub fn get_compiler() -> impl Compiler {
use wasmer_llvm_backend::LLVMCompiler;
LLVMCompiler::new()
}

View File

@ -0,0 +1,137 @@
use wasmer_runtime_core::{
compile_with, error::RuntimeError, imports, memory::Memory, typed_func::Func,
types::MemoryDescriptor, units::Pages, vm,
};
use wasmer_runtime_core_tests::{get_compiler, wat2wasm};
#[test]
fn imported_functions_forms() {
const MODULE: &str = r#"
(module
(type $type (func (param i32) (result i32)))
(import "env" "memory" (memory 1 1))
(import "env" "callback_fn" (func $callback_fn (type $type)))
(import "env" "callback_fn_with_vmctx" (func $callback_fn_with_vmctx (type $type)))
(import "env" "callback_fn_trap" (func $callback_fn_trap (type $type)))
(import "env" "callback_fn_trap_with_vmctx" (func $callback_fn_trap_with_vmctx (type $type)))
(func (export "function_fn") (type $type)
get_local 0
call $callback_fn)
(func (export "function_fn_with_vmctx") (type $type)
get_local 0
call $callback_fn_with_vmctx)
(func (export "function_fn_trap") (type $type)
get_local 0
call $callback_fn_trap)
(func (export "function_fn_trap_with_vmctx") (type $type)
get_local 0
call $callback_fn_trap_with_vmctx))
"#;
let wasm_binary = wat2wasm(MODULE.as_bytes()).expect("WAST not valid or malformed");
let module = compile_with(&wasm_binary, &get_compiler()).unwrap();
let memory_descriptor = MemoryDescriptor::new(Pages(1), Some(Pages(1)), false).unwrap();
let memory = Memory::new(memory_descriptor).unwrap();
const SHIFT: i32 = 10;
memory.view()[0].set(SHIFT);
let import_object = imports! {
"env" => {
"memory" => memory.clone(),
"callback_fn" => Func::new(callback_fn),
"callback_fn_with_vmctx" => Func::new(callback_fn_with_vmctx),
"callback_fn_trap" => Func::new(callback_fn_trap),
"callback_fn_trap_with_vmctx" => Func::new(callback_fn_trap_with_vmctx),
},
};
let instance = module.instantiate(&import_object).unwrap();
macro_rules! call_and_assert {
($function:ident, $expected_value:expr) => {
let $function: Func<i32, i32> = instance.func(stringify!($function)).unwrap();
let result = $function.call(1);
match (result, $expected_value) {
(Ok(value), expected_value) => assert_eq!(
Ok(value),
expected_value,
concat!("Expected right when calling `", stringify!($function), "`.")
),
(
Err(RuntimeError::Error { data }),
Err(RuntimeError::Error {
data: expected_data,
}),
) => {
if let (Some(data), Some(expected_data)) = (
data.downcast_ref::<&str>(),
expected_data.downcast_ref::<&str>(),
) {
assert_eq!(
data, expected_data,
concat!("Expected right when calling `", stringify!($function), "`.")
)
} else if let (Some(data), Some(expected_data)) = (
data.downcast_ref::<String>(),
expected_data.downcast_ref::<String>(),
) {
assert_eq!(
data, expected_data,
concat!("Expected right when calling `", stringify!($function), "`.")
)
} else {
assert!(false, "Unexpected error, cannot compare it.")
}
}
(result, expected_value) => assert!(
false,
format!(
"Unexpected assertion for `{}`: left = `{:?}`, right = `{:?}`.",
stringify!($function),
result,
expected_value
)
),
}
};
}
call_and_assert!(function_fn, Ok(2));
call_and_assert!(function_fn_with_vmctx, Ok(2 + SHIFT));
call_and_assert!(
function_fn_trap,
Err(RuntimeError::Error {
data: Box::new(format!("foo {}", 1))
})
);
call_and_assert!(
function_fn_trap_with_vmctx,
Err(RuntimeError::Error {
data: Box::new(format!("baz {}", 2 + SHIFT))
})
);
}
fn callback_fn(n: i32) -> Result<i32, ()> {
Ok(n + 1)
}
fn callback_fn_with_vmctx(vmctx: &mut vm::Ctx, n: i32) -> Result<i32, ()> {
let memory = vmctx.memory(0);
let shift: i32 = memory.view()[0].get();
Ok(shift + n + 1)
}
fn callback_fn_trap(n: i32) -> Result<i32, String> {
Err(format!("foo {}", n))
}
fn callback_fn_trap_with_vmctx(vmctx: &mut vm::Ctx, n: i32) -> Result<i32, String> {
let memory = vmctx.memory(0);
let shift: i32 = memory.view()[0].get();
Err(format!("baz {}", shift + n + 1))
}

View File

@ -1,6 +1,6 @@
[package] [package]
name = "wasmer-runtime-core" name = "wasmer-runtime-core"
version = "0.6.0" version = "0.9.0"
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>"]
@ -8,47 +8,46 @@ repository = "https://github.com/wasmerio/wasmer"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
nix = "0.15.0" nix = "0.15"
page_size = "0.4.1" page_size = "0.4"
wasmparser = "0.35.1" wasmparser = "0.39.1"
parking_lot = "0.9.0" parking_lot = "0.9"
lazy_static = "1.3.0" lazy_static = "1.4"
errno = "0.2.4" errno = "0.2"
libc = "0.2.60" libc = "0.2.60"
hex = "0.3.2" hex = "0.3"
smallvec = "0.6.10" smallvec = "0.6"
bincode = "1.1" bincode = "1.1"
colored = "1.8"
[dependencies.indexmap] [dependencies.indexmap]
version = "1.0.2" version = "1.2"
features = ["serde-1"] features = ["serde-1"]
# Dependencies for caching. # Dependencies for caching.
[dependencies.serde] [dependencies.serde]
version = "1.0.99" version = "1.0"
# This feature is required for serde to support serializing/deserializing reference counted pointers (e.g. Rc and Arc). # This feature is required for serde to support serializing/deserializing reference counted pointers (e.g. Rc and Arc).
features = ["rc"] features = ["rc"]
[dependencies.serde_derive] [dependencies.serde_derive]
version = "1.0.98" version = "1.0"
[dependencies.serde_bytes] [dependencies.serde_bytes]
version = "0.11.2" version = "0.11"
[dependencies.serde-bench] [dependencies.serde-bench]
version = "0.0.7" version = "0.0.7"
[dependencies.blake2b_simd] [dependencies.blake2b_simd]
version = "0.5.6" version = "0.5"
[dependencies.digest] [dependencies.digest]
version = "0.8.1" version = "0.8"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.8", features = ["memoryapi"] } winapi = { version = "0.3", features = ["memoryapi"] }
[dev-dependencies] [dev-dependencies]
field-offset = "0.1.1" field-offset = "0.1"
[build-dependencies] [build-dependencies]
blake2b_simd = "0.5.6" blake2b_simd = "0.5"
rustc_version = "0.2.3" rustc_version = "0.2"
cc = "1.0" cc = "1.0"
[features] [features]
@ -58,4 +57,4 @@ trace = ["debug"]
"backend-cranelift" = [] "backend-cranelift" = []
"backend-singlepass" = [] "backend-singlepass" = []
"backend-llvm" = [] "backend-llvm" = []
managed = [] managed = []

View File

@ -1,21 +1,21 @@
<p align="center"> <p align="center">
<a href="https://wasmer.io" target="_blank" rel="noopener noreferrer"> <a href="https://wasmer.io" target="_blank" rel="noopener noreferrer">
<img width="400" src="https://raw.githubusercontent.com/wasmerio/wasmer/master/logo.png" alt="Wasmer logo"> <img width="300" src="https://raw.githubusercontent.com/wasmerio/wasmer/master/logo.png" alt="Wasmer logo">
</a> </a>
</p> </p>
<p align="center"> <p align="center">
<a href="https://circleci.com/gh/wasmerio/wasmer/"> <a href="https://dev.azure.com/wasmerio/wasmer/_build/latest?definitionId=3&branchName=master">
<img src="https://img.shields.io/circleci/project/github/wasmerio/wasmer/master.svg" alt="Build Status"> <img src="https://img.shields.io/azure-devops/build/wasmerio/wasmer/3.svg?style=flat-square" alt="Build Status">
</a> </a>
<a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE"> <a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/wasmerio/wasmer.svg" alt="License"> <img src="https://img.shields.io/github/license/wasmerio/wasmer.svg?style=flat-square" alt="License">
</a> </a>
<a href="https://spectrum.chat/wasmer"> <a href="https://spectrum.chat/wasmer">
<img src="https://withspectrum.github.io/badge/badge.svg" alt="Join the Wasmer Community"> <img src="https://withspectrum.github.io/badge/badge.svg" alt="Join the Wasmer Community">
</a> </a>
<a href="https://crates.io/crates/wasmer-runtime-core"> <a href="https://crates.io/crates/wasmer-runtime-core">
<img src="https://img.shields.io/crates/d/wasmer-runtime-core.svg" alt="Number of downloads from crates.io"> <img src="https://img.shields.io/crates/d/wasmer-runtime-core.svg?style=flat-square" alt="Number of downloads from crates.io">
</a> </a>
<a href="https://docs.rs/wasmer-runtime-core"> <a href="https://docs.rs/wasmer-runtime-core">
<img src="https://docs.rs/wasmer-runtime-core/badge.svg" alt="Read our API documentation"> <img src="https://docs.rs/wasmer-runtime-core/badge.svg" alt="Read our API documentation">
@ -25,7 +25,8 @@
# Wasmer Runtime Core # Wasmer Runtime Core
Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully
compatible with Emscripten, Rust and Go. [Learn compatible with WASI, Emscripten, Rust and Go. [Learn
more](https://github.com/wasmerio/wasmer). more](https://github.com/wasmerio/wasmer).
This crate represents the core of the runtime. This crate represents the core of the runtime. Consider
[`wasmer-runtime`] for higher level APIs.

Some files were not shown because too many files have changed in this diff Show More