mirror of
https://github.com/fluencelabs/wasmer
synced 2024-12-13 06:15:33 +00:00
Merge branch 'master' into doc-runtime-c-api
# Conflicts: # lib/runtime-c-api/src/lib.rs
This commit is contained in:
commit
19eba19603
@ -12,6 +12,14 @@ run_install_dependencies: &run_install_dependencies
|
||||
|
||||
version: 2
|
||||
jobs:
|
||||
changelog:
|
||||
docker:
|
||||
- image: docker:stable-git
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
command: ! git diff --exit-code CHANGELOG.md
|
||||
|
||||
# Job used for testing
|
||||
lint:
|
||||
docker:
|
||||
@ -21,7 +29,8 @@ jobs:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v6-lint-{{ arch }}-{{ checksum "Cargo.lock" }}
|
||||
- v8-lint-{{ arch }}-{{ checksum "Cargo.lock" }}
|
||||
- v8-lint-{{ arch }}
|
||||
- <<: *run_install_dependencies
|
||||
- run:
|
||||
name: Install lint deps
|
||||
@ -40,7 +49,7 @@ jobs:
|
||||
- target/debug/.fingerprint
|
||||
- target/debug/build
|
||||
- target/debug/deps
|
||||
key: v6-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}
|
||||
key: v8-lint-{{ arch }}-{{ checksum "Cargo.lock" }}
|
||||
|
||||
test:
|
||||
docker:
|
||||
@ -50,14 +59,17 @@ jobs:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v6-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}
|
||||
- v8-test-cargo-cache-linux-stable-{{ arch }}-{{ checksum "Cargo.lock" }}
|
||||
- v8-test-cargo-cache-linux-stable-{{ arch }}
|
||||
- <<: *run_install_dependencies
|
||||
- run:
|
||||
name: Tests
|
||||
command: make test
|
||||
- run:
|
||||
name: Emscripten Tests
|
||||
command: make test-emscripten
|
||||
command: |
|
||||
make test-emscripten-clif
|
||||
make test-emscripten-llvm
|
||||
- run:
|
||||
name: Integration Tests
|
||||
command: make integration-tests
|
||||
@ -67,7 +79,7 @@ jobs:
|
||||
- target/debug/.fingerprint
|
||||
- target/debug/build
|
||||
- target/debug/deps
|
||||
key: v6-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}
|
||||
key: v8-test-cargo-cache-linux-stable-{{ arch }}-{{ checksum "Cargo.lock" }}
|
||||
|
||||
test-macos:
|
||||
macos:
|
||||
@ -76,7 +88,8 @@ jobs:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v6-cargo-cache-darwin-{{ arch }}-{{ checksum "Cargo.lock" }}
|
||||
- v8-cargo-cache-darwin-stable-{{ arch }}-{{ checksum "Cargo.lock" }}
|
||||
- v8-cargo-cache-darwin-stable-{{ arch }}
|
||||
- run:
|
||||
name: Install crate dependencies
|
||||
command: |
|
||||
@ -90,7 +103,7 @@ jobs:
|
||||
- run:
|
||||
name: Install Rust
|
||||
command: |
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||
curl -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
export PATH="$HOME/.cargo/bin:$PATH"
|
||||
cargo --version
|
||||
- run:
|
||||
@ -112,7 +125,8 @@ jobs:
|
||||
# We increase the ulimit for fixing cargo unclosed files in mac
|
||||
ulimit -n 8000
|
||||
sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680
|
||||
make test-emscripten
|
||||
make test-emscripten-clif
|
||||
make test-emscripten-llvm
|
||||
- run:
|
||||
name: Integration Tests
|
||||
command: |
|
||||
@ -129,22 +143,30 @@ jobs:
|
||||
- target/release/.fingerprint
|
||||
- target/release/build
|
||||
- target/release/deps
|
||||
key: v6-cargo-cache-darwin-{{ arch }}-{{ checksum "Cargo.lock" }}
|
||||
key: v8-cargo-cache-darwin-stable-{{ arch }}-{{ checksum "Cargo.lock" }}
|
||||
|
||||
test-and-build:
|
||||
docker:
|
||||
- image: circleci/rust:latest
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: "Pull dependencies"
|
||||
command: |
|
||||
git submodule init
|
||||
git submodule update --remote
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v6-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}
|
||||
- v8-cargo-cache-linux-nightly-{{ arch }}-{{ checksum "Cargo.lock" }}
|
||||
- v8-cargo-cache-linux-nightly-{{ arch }}
|
||||
- run:
|
||||
name: Install dependencies
|
||||
command: |
|
||||
sudo apt-get install -y cmake
|
||||
curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
|
||||
tar xf clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
|
||||
# Use rust nightly (for singlepass, for now)
|
||||
- run: rustup default nightly-2019-04-11
|
||||
- run:
|
||||
name: Tests
|
||||
command: |
|
||||
@ -154,18 +176,21 @@ jobs:
|
||||
name: Emscripten Tests
|
||||
command: |
|
||||
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
|
||||
make test-emscripten
|
||||
make test-emscripten-clif
|
||||
make test-emscripten-llvm
|
||||
- run:
|
||||
name: Release Build
|
||||
command: |
|
||||
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
|
||||
make release
|
||||
make production-release
|
||||
cargo build --release --manifest-path wapm-cli/Cargo.toml --features telemetry
|
||||
mkdir -p artifacts
|
||||
VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2)
|
||||
# GIT_VERSION=$(git describe --exact-match --tags)
|
||||
echo "${VERSION}" >> artifacts/version
|
||||
echo "${CIRCLE_TAG}" >> artifacts/git_version
|
||||
cp ./target/release/wasmer ./artifacts/$(./binary-name.sh)
|
||||
make build-install
|
||||
cp ./wasmer.tar.gz ./artifacts/$(./binary-name.sh)
|
||||
- run:
|
||||
name: Debug flag checked
|
||||
command: |
|
||||
@ -183,16 +208,25 @@ jobs:
|
||||
- target/release/.fingerprint
|
||||
- target/release/build
|
||||
- target/release/deps
|
||||
key: v6-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}
|
||||
- wapm-cli/target/release/.fingerprint
|
||||
- wapm-cli/target/release/build
|
||||
- wapm-cli/target/release/deps
|
||||
key: v8-cargo-cache-linux-nightly-{{ arch }}-{{ checksum "Cargo.lock" }}
|
||||
|
||||
test-and-build-macos:
|
||||
macos:
|
||||
xcode: "9.0"
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: "Pull dependencies"
|
||||
command: |
|
||||
git submodule init
|
||||
git submodule update --remote
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v6-cargo-cache-darwin-{{ arch }}-{{ checksum "Cargo.lock" }}
|
||||
- v8-cargo-cache-darwin-nightly-{{ arch }}-{{ checksum "Cargo.lock" }}
|
||||
- v8-cargo-cache-darwin-nightly-{{ arch }}
|
||||
- run:
|
||||
name: Install crate dependencies
|
||||
command: |
|
||||
@ -206,9 +240,15 @@ jobs:
|
||||
- run:
|
||||
name: Install Rust
|
||||
command: |
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||
curl -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain nightly
|
||||
export PATH="$HOME/.cargo/bin:$PATH"
|
||||
cargo --version
|
||||
# Use rust nightly (for singlepass, for now)
|
||||
# - run:
|
||||
# name: Install Rust nightly
|
||||
# command: |
|
||||
# export PATH="$HOME/.rustup/bin:$PATH"
|
||||
# rustup default nightly-2019-04-11
|
||||
- run:
|
||||
name: Tests
|
||||
command: |
|
||||
@ -228,18 +268,21 @@ jobs:
|
||||
# We increase the ulimit for fixing cargo unclosed files in mac
|
||||
ulimit -n 8000
|
||||
sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680
|
||||
make test-emscripten
|
||||
make test-emscripten-clif
|
||||
make test-emscripten-singlepass
|
||||
- run:
|
||||
name: Release Build
|
||||
command: |
|
||||
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
|
||||
export PATH="$HOME/.cargo/bin:$PATH"
|
||||
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/"
|
||||
make release
|
||||
make production-release
|
||||
cargo build --release --manifest-path wapm-cli/Cargo.toml --features telemetry
|
||||
mkdir -p artifacts
|
||||
make build-install
|
||||
cp ./wasmer.tar.gz ./artifacts/$(./binary-name.sh)
|
||||
# VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2)
|
||||
# echo "${VERSION}" >> artifacts/version
|
||||
cp ./target/release/wasmer ./artifacts/$(./binary-name.sh)
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
@ -253,7 +296,10 @@ jobs:
|
||||
- target/release/.fingerprint
|
||||
- target/release/build
|
||||
- target/release/deps
|
||||
key: v6-cargo-cache-darwin-{{ arch }}-{{ checksum "Cargo.lock" }}
|
||||
- wapm-cli/target/release/.fingerprint
|
||||
- wapm-cli/target/release/build
|
||||
- wapm-cli/target/release/deps
|
||||
key: v8-cargo-cache-darwin-nightly-{ arch }}-{{ checksum "Cargo.lock" }}
|
||||
|
||||
test-rust-nightly:
|
||||
docker:
|
||||
@ -262,27 +308,28 @@ jobs:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v6-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}-nightly
|
||||
- v8-cargo-cache-linux-nightly-{{ arch }}-{{ checksum "Cargo.lock" }}
|
||||
- v8-cargo-cache-linux-nightly-{{ arch }}
|
||||
- run:
|
||||
name: Install dependencies
|
||||
command: |
|
||||
sudo apt-get install -y cmake
|
||||
curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
|
||||
tar xf clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
|
||||
- run: rustup default nightly-2019-02-27
|
||||
- run: rustup default nightly-2019-04-11
|
||||
- run: |
|
||||
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
|
||||
make test
|
||||
make test-nightly
|
||||
make test-emscripten
|
||||
make test-emscripten-nightly
|
||||
make test-singlepass
|
||||
make test-emscripten-clif
|
||||
make test-emscripten-singlepass
|
||||
- save_cache:
|
||||
paths:
|
||||
- /usr/local/cargo/registry
|
||||
- target/debug/.fingerprint
|
||||
- target/debug/build
|
||||
- target/debug/deps
|
||||
key: v6-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}-nightly
|
||||
key: v8-cargo-cache-linux-nightly-{{ arch }}-{{ checksum "Cargo.lock" }}-nightly
|
||||
|
||||
publish-github-release:
|
||||
docker:
|
||||
@ -330,6 +377,7 @@ workflows:
|
||||
version: 2
|
||||
main:
|
||||
jobs:
|
||||
- changelog
|
||||
- lint
|
||||
- test:
|
||||
filters:
|
||||
|
6
.dockerignore
Normal file
6
.dockerignore
Normal file
@ -0,0 +1,6 @@
|
||||
# Ignore everything
|
||||
**
|
||||
!lib/**
|
||||
!src/**
|
||||
!Cargo.toml
|
||||
!Cargo.lock
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,3 +4,4 @@
|
||||
.DS_Store
|
||||
.idea
|
||||
**/.vscode
|
||||
install/
|
||||
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "wapm-cli"]
|
||||
path = wapm-cli
|
||||
url = https://github.com/wasmerio/wapm-cli.git
|
54
CHANGELOG.md
Normal file
54
CHANGELOG.md
Normal file
@ -0,0 +1,54 @@
|
||||
# Changelog
|
||||
|
||||
All PRs to the Wasmer repository must add to this file.
|
||||
|
||||
Blocks of changes will separated by version increments.
|
||||
|
||||
## **[Unreleased]**
|
||||
|
||||
- [#269](https://github.com/wasmerio/wasmer/pull/269) Add better runtime docs
|
||||
- [#432](https://github.com/wasmerio/wasmer/pull/432) Fix returned value of `wasmer_last_error_message` in the runtime C API
|
||||
- [#429](https://github.com/wasmerio/wasmer/pull/429) Get wasi::path_filestat_get working for some programs; misc. minor WASI FS improvements
|
||||
- [#413](https://github.com/wasmerio/wasmer/pull/413) Update LLVM backend to use new parser codegen traits
|
||||
|
||||
## 0.4.1 - 2018-05-06
|
||||
|
||||
- [#426](https://github.com/wasmerio/wasmer/pull/426) Update wapm-cli submodule, bump version to 0.4.1
|
||||
- [#422](https://github.com/wasmerio/wasmer/pull/422) Improved Emscripten functions to run optipng and pngquant compiled to wasm
|
||||
- [#409](https://github.com/wasmerio/wasmer/pull/409) Improved Emscripten functions to run JavascriptCore compiled to wasm
|
||||
- [#399](https://github.com/wasmerio/wasmer/pull/399) Add example of using a plugin extended from WASI
|
||||
- [#397](https://github.com/wasmerio/wasmer/pull/397) Fix WASI fs abstraction to work on Windows
|
||||
- [#390](https://github.com/wasmerio/wasmer/pull/390) Pin released wapm version and add it as a git submodule
|
||||
- [#408](https://github.com/wasmerio/wasmer/pull/408) Add images to windows installer and update installer to add wapm bin directory to path
|
||||
|
||||
## 0.4.0 - 2018-04-23
|
||||
|
||||
- [#383](https://github.com/wasmerio/wasmer/pull/383) Hook up wasi exit code to wasmer cli.
|
||||
- [#382](https://github.com/wasmerio/wasmer/pull/382) Improve error message on `--backend` flag to only suggest currently enabled backends
|
||||
- [#381](https://github.com/wasmerio/wasmer/pull/381) Allow retrieving propagated user errors.
|
||||
- [#379](https://github.com/wasmerio/wasmer/pull/379) Fix small return types from imported functions.
|
||||
- [#371](https://github.com/wasmerio/wasmer/pull/371) Add more Debug impl for WASI types
|
||||
- [#368](https://github.com/wasmerio/wasmer/pull/368) Fix issue with write buffering
|
||||
- [#343](https://github.com/wasmerio/wasmer/pull/343) Implement preopened files for WASI and fix aligment issue when accessing WASI memory
|
||||
- [#367](https://github.com/wasmerio/wasmer/pull/367) Add caching support to the LLVM backend.
|
||||
- [#366](https://github.com/wasmerio/wasmer/pull/366) Remove `UserTrapper` trait to fix [#365](https://github.com/wasmerio/wasmer/issues/365).
|
||||
- [#348](https://github.com/wasmerio/wasmer/pull/348) Refactor internal runtime ↔️ backend abstraction.
|
||||
- [#355](https://github.com/wasmerio/wasmer/pull/355) Misc changes to `Cargo.toml`s for publishing
|
||||
- [#352](https://github.com/wasmerio/wasmer/pull/352) Bump version numbers to 0.3.0
|
||||
- [#351](https://github.com/wasmerio/wasmer/pull/351) Add hidden option to specify wasm program name (can be used to improve error messages)
|
||||
- [#350](https://github.com/wasmerio/wasmer/pull/350) Enforce that CHANGELOG.md is updated through CI.
|
||||
- [#349](https://github.com/wasmerio/wasmer/pull/349) Add [CHANGELOG.md](https://github.com/wasmerio/wasmer/blob/master/CHANGELOG.md).
|
||||
|
||||
## 0.3.0 - 2018-04-12
|
||||
|
||||
- [#276](https://github.com/wasmerio/wasmer/pull/276) [#288](https://github.com/wasmerio/wasmer/pull/288) [#344](https://github.com/wasmerio/wasmer/pull/344) Use new singlepass backend (with the `--backend=singlepass` when running Wasmer)
|
||||
- [#338](https://github.com/wasmerio/wasmer/pull/338) Actually catch traps/panics/etc when using a typed func.
|
||||
- [#325](https://github.com/wasmerio/wasmer/pull/325) Fixed func_index in debug mode
|
||||
- [#323](https://github.com/wasmerio/wasmer/pull/323) Add validate subcommand to validate Wasm files
|
||||
- [#321](https://github.com/wasmerio/wasmer/pull/321) Upgrade to Cranelift 0.3.0
|
||||
- [#319](https://github.com/wasmerio/wasmer/pull/319) Add Export and GlobalDescriptor to Runtime API
|
||||
- [#310](https://github.com/wasmerio/wasmer/pull/310) Cleanup warnings
|
||||
- [#299](https://github.com/wasmerio/wasmer/pull/299) [#300](https://github.com/wasmerio/wasmer/pull/300) [#301](https://github.com/wasmerio/wasmer/pull/301) [#303](https://github.com/wasmerio/wasmer/pull/303) [#304](https://github.com/wasmerio/wasmer/pull/304) [#305](https://github.com/wasmerio/wasmer/pull/305) [#306](https://github.com/wasmerio/wasmer/pull/306) [#307](https://github.com/wasmerio/wasmer/pull/307) Add support for WASI 🎉
|
||||
- [#286](https://github.com/wasmerio/wasmer/pull/286) Add extend to imports
|
||||
- [#278](https://github.com/wasmerio/wasmer/pull/278) Add versioning to cache
|
||||
- [#250](https://github.com/wasmerio/wasmer/pull/250) Setup bors
|
692
Cargo.lock
generated
692
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
23
Cargo.toml
23
Cargo.toml
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer"
|
||||
version = "0.2.1"
|
||||
version = "0.4.1"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
edition = "2018"
|
||||
repository = "https://github.com/wasmerio/wasmer"
|
||||
@ -24,25 +24,34 @@ structopt = "0.2.11"
|
||||
wabt = "0.7.2"
|
||||
hashbrown = "0.1.8"
|
||||
wasmer-clif-backend = { path = "lib/clif-backend" }
|
||||
wasmer-dynasm-backend = { path = "lib/dynasm-backend", optional = true }
|
||||
wasmer-singlepass-backend = { path = "lib/singlepass-backend", optional = true }
|
||||
wasmer-middleware-common = { path = "lib/middleware-common" }
|
||||
wasmer-runtime = { path = "lib/runtime" }
|
||||
wasmer-runtime-abi = { path = "lib/runtime-abi", optional = true }
|
||||
wasmer-runtime-core = { path = "lib/runtime-core" }
|
||||
wasmer-emscripten = { path = "lib/emscripten" }
|
||||
wasmer-llvm-backend = { path = "lib/llvm-backend", optional = true }
|
||||
wasmer-wasi = { path = "lib/wasi", optional = true }
|
||||
|
||||
[workspace]
|
||||
members = ["lib/clif-backend", "lib/dynasm-backend", "lib/runtime", "lib/runtime-abi", "lib/runtime-core", "lib/emscripten", "lib/spectests", "lib/win-exception-handler", "lib/runtime-c-api", "lib/llvm-backend"]
|
||||
members = ["lib/clif-backend", "lib/singlepass-backend", "lib/runtime", "lib/runtime-abi", "lib/runtime-core", "lib/emscripten", "lib/spectests", "lib/win-exception-handler", "lib/runtime-c-api", "lib/llvm-backend", "lib/wasi", "lib/middleware-common", "examples/plugin-for-example"]
|
||||
|
||||
[build-dependencies]
|
||||
wabt = "0.7.2"
|
||||
glob = "0.2.11"
|
||||
rustc_version = "0.2.3"
|
||||
|
||||
[features]
|
||||
default = ["fast-tests"]
|
||||
debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"]
|
||||
default = ["fast-tests", "wasi"]
|
||||
debug = ["wasmer-runtime-core/debug"]
|
||||
extra-debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"]
|
||||
# This feature will allow cargo test to run much faster
|
||||
fast-tests = []
|
||||
llvm = ["wasmer-llvm-backend"]
|
||||
dynasm = ["wasmer-dynasm-backend"]
|
||||
"backend:llvm" = ["wasmer-llvm-backend"]
|
||||
"backend:singlepass" = ["wasmer-singlepass-backend"]
|
||||
wasi = ["wasmer-wasi"]
|
||||
vfs = ["wasmer-runtime-abi"]
|
||||
|
||||
[[example]]
|
||||
name = "plugin"
|
||||
crate-type = ["bin"]
|
||||
|
25
Dockerfile
Normal file
25
Dockerfile
Normal file
@ -0,0 +1,25 @@
|
||||
FROM circleci/rust:1.33.0-stretch as wasmer-build-env
|
||||
RUN sudo apt-get update && \
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
cmake \
|
||||
&& sudo rm -rf /var/lib/apt/lists/*
|
||||
RUN curl -SL https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz \
|
||||
| tar -xJC /home/circleci
|
||||
ENV LLVM_SYS_70_PREFIX /home/circleci/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/
|
||||
|
||||
FROM wasmer-build-env AS wasmer-debug-env
|
||||
RUN sudo apt-get update && \
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
valgrind \
|
||||
&& sudo rm -rf /var/lib/apt/lists/*
|
||||
|
||||
FROM wasmer-build-env AS wasmer-build
|
||||
WORKDIR /home/circleci/wasmer
|
||||
COPY . /home/circleci/wasmer
|
||||
RUN sudo chmod -R 777 .
|
||||
RUN cargo build --release
|
||||
|
||||
FROM debian:stretch AS wasmer
|
||||
WORKDIR /root/
|
||||
COPY --from=wasmer-build /home/circleci/wasmer/target/release/wasmer .
|
||||
ENTRYPOINT ["./wasmer"]
|
4
LICENSE
4
LICENSE
@ -1,4 +1,6 @@
|
||||
Copyright (c) 2019 Syrus Akbary
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Wasmer, Inc. and its affiliates.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
42
Makefile
42
Makefile
@ -25,6 +25,7 @@ integration-tests: release
|
||||
echo "Running Integration Tests"
|
||||
./integration_tests/lua/test.sh
|
||||
./integration_tests/nginx/test.sh
|
||||
./integration_tests/cowsay/test.sh
|
||||
|
||||
lint:
|
||||
cargo fmt --all -- --check
|
||||
@ -32,32 +33,59 @@ lint:
|
||||
|
||||
precommit: lint test
|
||||
|
||||
build-install:
|
||||
mkdir -p ./install/bin
|
||||
cp ./wapm-cli/target/release/wapm ./install/bin/
|
||||
cp ./target/release/wasmer ./install/bin/
|
||||
tar -C ./install -zcvf wasmer.tar.gz bin/wapm bin/wasmer
|
||||
|
||||
# For installing the contents locally
|
||||
do-install:
|
||||
tar -C ~/.wasmer -zxvf wasmer.tar.gz
|
||||
|
||||
test:
|
||||
# We use one thread so the emscripten stdouts doesn't collide
|
||||
cargo test --all --exclude wasmer-runtime-c-api --exclude wasmer-emscripten --exclude wasmer-spectests --exclude wasmer-dynasm-backend -- $(runargs)
|
||||
cargo test --all --exclude wasmer-runtime-c-api --exclude wasmer-emscripten --exclude wasmer-spectests --exclude wasmer-singlepass-backend -- $(runargs)
|
||||
# cargo test --all --exclude wasmer-emscripten -- --test-threads=1 $(runargs)
|
||||
cargo test --manifest-path lib/spectests/Cargo.toml --features clif
|
||||
cargo test --manifest-path lib/spectests/Cargo.toml --features llvm
|
||||
cargo build -p wasmer-runtime-c-api
|
||||
cargo test -p wasmer-runtime-c-api -- --nocapture
|
||||
|
||||
test-nightly:
|
||||
cargo test --manifest-path lib/spectests/Cargo.toml --features dynasm
|
||||
test-singlepass:
|
||||
cargo test --manifest-path lib/spectests/Cargo.toml --features singlepass
|
||||
|
||||
test-emscripten:
|
||||
cargo test --manifest-path lib/emscripten/Cargo.toml --features clif -- --test-threads=1 $(runargs)
|
||||
test-emscripten-llvm:
|
||||
cargo test --manifest-path lib/emscripten/Cargo.toml --features llvm -- --test-threads=1 $(runargs)
|
||||
|
||||
test-emscripten-nightly:
|
||||
cargo test --manifest-path lib/emscripten/Cargo.toml --features dynasm -- --test-threads=1 $(runargs)
|
||||
test-emscripten-clif:
|
||||
cargo test --manifest-path lib/emscripten/Cargo.toml --features clif -- --test-threads=1 $(runargs)
|
||||
|
||||
test-emscripten-singlepass:
|
||||
cargo test --manifest-path lib/emscripten/Cargo.toml --features singlepass -- --test-threads=1 $(runargs)
|
||||
|
||||
singlepass-debug-release:
|
||||
cargo +nightly build --features "backend:singlepass debug" --release
|
||||
|
||||
singlepass-release:
|
||||
cargo +nightly build --features "backend:singlepass" --release
|
||||
|
||||
singlepass-build:
|
||||
cargo +nightly build --features "backend:singlepass debug"
|
||||
|
||||
release:
|
||||
# If you are in OS-X, you will need mingw-w64 for cross compiling to windows
|
||||
# brew install mingw-w64
|
||||
cargo build --release
|
||||
|
||||
production-release:
|
||||
cargo build --release --features backend:singlepass,backend:llvm
|
||||
|
||||
debug-release:
|
||||
cargo build --release --features "debug"
|
||||
|
||||
extra-debug-release:
|
||||
cargo build --release --features "extra-debug"
|
||||
|
||||
publish-release:
|
||||
ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete ${VERSION} ./artifacts/
|
||||
|
45
README.md
45
README.md
@ -26,23 +26,53 @@ Install Wasmer with:
|
||||
curl https://get.wasmer.io -sSfL | sh
|
||||
```
|
||||
|
||||
_**NEW ✨**: You can now embed Wasmer in your Rust application, check our [example repo](https://github.com/wasmerio/wasmer-rust-example) to see how!_
|
||||
Wasmer runtime can also be embedded in different languages, so you can use WebAssembly anywhere ✨:
|
||||
|
||||
* [**Rust**](https://github.com/wasmerio/wasmer-rust-example)
|
||||
* [**C/C++**](https://github.com/wasmerio/wasmer-c-api)
|
||||
* [**🐘 PHP**](https://github.com/wasmerio/php-ext-wasm)
|
||||
* [**🐍 Python**](https://github.com/wasmerio/python-ext-wasm)
|
||||
* [**💎 Ruby**](https://github.com/wasmerio/ruby-ext-wasm)
|
||||
|
||||
### Usage
|
||||
|
||||
Wasmer can execute both the standard binary format (`.wasm`) and the text
|
||||
format defined by the WebAssembly reference interpreter (`.wat`).
|
||||
|
||||
Once installed, you will be able to run any WebAssembly files (_including nginx and Lua!_):
|
||||
Once installed, you will be able to run any WebAssembly files (_including Lua, PHP, SQLite and nginx!_):
|
||||
|
||||
```sh
|
||||
# Run Lua
|
||||
wasmer run examples/lua.wasm
|
||||
|
||||
# Run PHP
|
||||
wasmer run examples/php.wasm
|
||||
|
||||
# Run SQLite
|
||||
wasmer run examples/sqlite.wasm
|
||||
|
||||
# Run nginx
|
||||
wasmer run examples/nginx/nginx.wasm -- -p examples/nginx -c nginx.conf
|
||||
```
|
||||
|
||||
#### With WAPM
|
||||
|
||||
Installing Wasmer through `wasmer.io` includes
|
||||
[wapm](https://github.com/wasmerio/wapm-cli), the WebAssembly package manager.
|
||||
|
||||
Wapm allows you to easily download, run, and distribute WebAssembly binaries.
|
||||
|
||||
```sh
|
||||
# Install cowsay globally
|
||||
wapm install -g cowsay
|
||||
|
||||
# Run cowsay
|
||||
wapm run cowsay "Hello, world!"
|
||||
```
|
||||
|
||||
For more information about wapm, check out the [website](https://www.wapm.io)
|
||||
and this [example program](https://github.com/wapm-packages/rust-wasi-example).
|
||||
|
||||
## Code Structure
|
||||
|
||||
Wasmer is structured into different directories:
|
||||
@ -70,6 +100,7 @@ Please select your operating system:
|
||||
|
||||
- [macOS](#macos)
|
||||
- [Debian-based Linuxes](#debian-based-linuxes)
|
||||
- [FreeBSD](#freebsd)
|
||||
- [Microsoft Windows](#windows-msvc)
|
||||
|
||||
#### macOS
|
||||
@ -89,7 +120,13 @@ sudo port install cmake
|
||||
#### Debian-based Linuxes
|
||||
|
||||
```sh
|
||||
sudo apt install cmake
|
||||
sudo apt install cmake pkg-config libssl-dev
|
||||
```
|
||||
|
||||
#### FreeBSD
|
||||
|
||||
```sh
|
||||
pkg install cmake
|
||||
```
|
||||
|
||||
#### Windows (MSVC)
|
||||
@ -164,7 +201,7 @@ Below are some of the goals of this project (in order of priority):
|
||||
|
||||
- [x] It should be 100% compatible with the [WebAssembly spec tests](https://github.com/wasmerio/wasmer/tree/master/lib/spectests/spectests)
|
||||
- [x] It should be fast _(partially achieved)_
|
||||
- [ ] Support WASI _(in the works)_
|
||||
- [x] Support WASI - released in [0.3.0](https://github.com/wasmerio/wasmer/releases/tag/0.3.0)
|
||||
- [ ] Support Emscripten calls _(in the works)_
|
||||
- [ ] Support Rust ABI calls
|
||||
- [ ] Support Go ABI calls
|
||||
|
@ -23,8 +23,8 @@ initOS() {
|
||||
darwin) OS='darwin';;
|
||||
linux) OS='linux';;
|
||||
freebsd) OS='freebsd';;
|
||||
mingw*) OS='windows';;
|
||||
msys*) OS='windows';;
|
||||
# mingw*) OS='windows';;
|
||||
# msys*) OS='windows';;
|
||||
*) echo "OS ${OS} is not supported by this installation script"; exit 1;;
|
||||
esac
|
||||
}
|
||||
@ -34,11 +34,11 @@ initArch
|
||||
initOS
|
||||
|
||||
# determine install directory if required
|
||||
BINARY="wasmer-${OS}-${ARCH}"
|
||||
BINARY="wasmer-${OS}-${ARCH}.tar.gz"
|
||||
|
||||
# add .exe if on windows
|
||||
if [ "$OS" = "windows" ]; then
|
||||
BINARY="$BINARY.exe"
|
||||
fi
|
||||
# if [ "$OS" = "windows" ]; then
|
||||
# BINARY="$BINARY.exe"
|
||||
# fi
|
||||
|
||||
echo "${BINARY}"
|
||||
|
@ -2,6 +2,7 @@ status = [
|
||||
"ci/circleci: lint",
|
||||
"ci/circleci: test",
|
||||
"ci/circleci: test-macos",
|
||||
"ci/circleci: test-rust-nightly",
|
||||
"continuous-integration/appveyor/branch"
|
||||
]
|
||||
required_approvals = 1
|
||||
|
39
docs/docker.md
Normal file
39
docs/docker.md
Normal file
@ -0,0 +1,39 @@
|
||||
# Dockerfile Documentation
|
||||
The `Dockerfile` included in the project root directory could be used for development purposes or to build a small image containing the `wasmer` executable.
|
||||
|
||||
The `wasmer-build-env` stage in the Dockerfile contains the dependencies needed to compile Wasmer including LLVM.
|
||||
|
||||
The `wasmer-debug-env` stage adds the `valgrind` profiling tool to the `wasmer-build-env` stage.
|
||||
|
||||
The `wasmer-build` stage in the Dockerfile will copy the current directory, assuming the build context is the `wasmer` project, and build the project using `cargo build --release`.
|
||||
|
||||
The `wasmer` stage will copy the resulting `wasmer` executable from the `wasmer-build` stage into a new base image to create a smaller image containing `wasmer`.
|
||||
|
||||
## Example Usages
|
||||
|
||||
### Wasmer image
|
||||
1. From the `wasmer` project directory, build the image:
|
||||
`docker build -t wasmer --target=wasmer .`
|
||||
|
||||
2. List options:
|
||||
`docker run wasmer --help`
|
||||
|
||||
3. Mount a directory, and run an example wasm file:
|
||||
`docker run -v /Users/admin/Documents/wasmer-workspace:/root/wasmer-workspace wasmer run /root/wasmer-workspace/examples/hello.wasm`
|
||||
|
||||
### Profiling
|
||||
1. Build `wasmer-debug-env`:
|
||||
`docker build --tag=wasmer-debug-env --target wasmer-debug-env .`
|
||||
|
||||
2. Mount a directory from the host and run interactively:
|
||||
`docker run -it -v /Users/admin/Documents/wasmer-workspace:/home/circleci/wasmer-workspace wasmer-debug-env /bin/bash`
|
||||
|
||||
3. Inside the container, build `wasmer` and run profiling tool:
|
||||
```
|
||||
cd /home/circleci/wasmer-workspace/wasmer`
|
||||
cargo build
|
||||
valgrind --tool=callgrind --dump-instr=yes --collect-jumps=yes --simulate-cache=yes target/debug/wasmer run test.wasm
|
||||
```
|
||||
|
||||
The `callgrind.out` can be viewed with the `qcachegrind` tool on Mac OS (`brew install qcachegrind`).
|
||||
|
9
examples/README.md
Normal file
9
examples/README.md
Normal file
@ -0,0 +1,9 @@
|
||||
# WebAssembly Examples
|
||||
|
||||
In this directory you can find a set of different examples that can run on the Wasmer WebAssembly runtime:
|
||||
|
||||
* Cowsay (WASI ABI) [[source-code](https://github.com/wapm-packages/cowsay)] [[wapm-package](https://wapm.io/package/cowsay)]
|
||||
* Nginx (Emscripten ABI) [[source-code](https://github.com/wapm-packages/nginx)] [[wapm-package](https://wapm.io/package/nginx)]
|
||||
* Lua (Emscripten ABI) [[source-code](https://github.com/wapm-packages/lua)] [[wapm-package](https://wapm.io/package/lua)]
|
||||
* PHP (Emscripten ABI) [[source-code](https://github.com/wapm-packages/php)] [[wapm-package](https://wapm.io/package/php)]
|
||||
* SQLite (Emscripten ABI) [[source-code](https://github.com/wapm-packages/sqlite)] [[wapm-package](https://wapm.io/package/sqlite)]
|
BIN
examples/cowsay.wasm
Executable file
BIN
examples/cowsay.wasm
Executable file
Binary file not shown.
11
examples/exit.wat
Normal file
11
examples/exit.wat
Normal file
@ -0,0 +1,11 @@
|
||||
(module
|
||||
(import "wasi_unstable" "proc_exit" (func $proc_exit (param i32)))
|
||||
(export "_start" (func $_start))
|
||||
|
||||
(memory 10)
|
||||
(export "memory" (memory 0))
|
||||
|
||||
(func $_start
|
||||
(call $proc_exit (i32.const 7))
|
||||
)
|
||||
)
|
Binary file not shown.
BIN
examples/php.wasm
Normal file
BIN
examples/php.wasm
Normal file
Binary file not shown.
BIN
examples/plugin-for-example.wasm
Executable file
BIN
examples/plugin-for-example.wasm
Executable file
Binary file not shown.
7
examples/plugin-for-example/Cargo.toml
Normal file
7
examples/plugin-for-example/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "plugin-for-example"
|
||||
version = "0.1.0"
|
||||
authors = ["The Wasmer Engineering Team <enigneering@wasmer.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
43
examples/plugin-for-example/README.md
Normal file
43
examples/plugin-for-example/README.md
Normal file
@ -0,0 +1,43 @@
|
||||
# WASI plugin example
|
||||
|
||||
In this example we extend the imports of Wasmer's WASI ABI to demonstrate how custom plugins work.
|
||||
|
||||
See the `wasmer/examples/plugin.rs` file for the source code of the host system.
|
||||
|
||||
## Compiling
|
||||
_Attention Windows users: WASI target only works with the `nightly-x86_64-pc-windows-gnu` toolchain._
|
||||
```
|
||||
# Install an up to date version of Rust nightly
|
||||
# Add the target
|
||||
rustup target add wasm32-unknown-wasi
|
||||
# build it
|
||||
cargo +nightly build --release --target=wasm32-unknown-wasi
|
||||
# copy it to examples folder
|
||||
cp ../../target/wasm32-unknown-wasi/release/plugin-for-example.wasm ../
|
||||
```
|
||||
|
||||
## Running
|
||||
```
|
||||
# Go back to top level Wasmer dir
|
||||
cd ..
|
||||
# Run the example
|
||||
cargo run --example plugin
|
||||
```
|
||||
|
||||
## Inspecting the plugin
|
||||
```
|
||||
# Install wabt via wapm; installed globally with the `g` flag
|
||||
wapm install -g wabt
|
||||
# Turn the binary WASM file in to a readable WAT text file
|
||||
wapm run wasm2wat examples/plugin-for-example.wasm
|
||||
```
|
||||
|
||||
At the top of the file we can see which functions this plugin expects. Most are covered by WASI, but we handle the rest.
|
||||
|
||||
## Explanation
|
||||
|
||||
In this example, we instantiate a system with an extended (WASI)[wasi] ABI, allowing our program to rely on Wasmer's implementation of the syscalls defined by WASI as well as our own that we made. This allows us to use the full power of an existing ABI, like WASI, and give it super-powers for our specific use case.
|
||||
|
||||
Because the Rust WASI doesn't support the crate type of `cdylib`, we have to include a main function which we don't use. This is being discussed [here](https://github.com/WebAssembly/WASI/issues/24).
|
||||
|
||||
[wasi]: https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/
|
12
examples/plugin-for-example/src/main.rs
Normal file
12
examples/plugin-for-example/src/main.rs
Normal file
@ -0,0 +1,12 @@
|
||||
extern "C" {
|
||||
fn it_works() -> i32;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn plugin_entrypoint(n: i32) -> i32 {
|
||||
println!("Hello from inside WASI");
|
||||
let result = unsafe { it_works() };
|
||||
result + n
|
||||
}
|
||||
|
||||
pub fn main() {}
|
12
examples/plugin-for-example/wapm.toml
Normal file
12
examples/plugin-for-example/wapm.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "plugin-for-example"
|
||||
version = "0.1.0"
|
||||
description = "A plugin for our example system"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/wasmerio/wasmer/examples/plugin-for-example"
|
||||
license = "MIT"
|
||||
|
||||
[[module]]
|
||||
name = "plugin-for-example"
|
||||
source = "../../target/wasm32-unknown-wasi/release/plugin-for-example.wasm"
|
||||
abi = "none"
|
38
examples/plugin.rs
Normal file
38
examples/plugin.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use wasmer_runtime::{func, imports, instantiate};
|
||||
use wasmer_runtime_core::vm::Ctx;
|
||||
use wasmer_wasi::generate_import_object;
|
||||
|
||||
static PLUGIN_LOCATION: &'static str = "examples/plugin-for-example.wasm";
|
||||
|
||||
fn it_works(_ctx: &mut Ctx) -> i32 {
|
||||
println!("Hello from outside WASI");
|
||||
5
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Load the plugin data
|
||||
let wasm_bytes = std::fs::read(PLUGIN_LOCATION).expect(&format!(
|
||||
"Could not read in WASM plugin at {}",
|
||||
PLUGIN_LOCATION
|
||||
));
|
||||
|
||||
// WASI imports
|
||||
let mut base_imports = generate_import_object(vec![], vec![], vec![]);
|
||||
// env is the default namespace for extern functions
|
||||
let custom_imports = imports! {
|
||||
"env" => {
|
||||
"it_works" => func!(it_works),
|
||||
},
|
||||
};
|
||||
// The WASI imports object contains all required import functions for a WASI module to run.
|
||||
// Extend this imports with our custom imports containing "it_works" function so that our custom wasm code may run.
|
||||
base_imports.extend(custom_imports);
|
||||
let instance =
|
||||
instantiate(&wasm_bytes[..], &base_imports).expect("failed to instantiate wasm module");
|
||||
|
||||
// get a reference to the function "plugin_entrypoint" which takes an i32 and returns an i32
|
||||
let entry_point = instance.func::<(i32), i32>("plugin_entrypoint").unwrap();
|
||||
// call the "entry_point" function in WebAssembly with the number "2" as the i32 argument
|
||||
let result = entry_point.call(2).expect("failed to execute plugin");
|
||||
println!("result: {}", result);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
(module
|
||||
(func $main (export "main") (result i32)
|
||||
(func $main (export "main")
|
||||
(local $count i32)
|
||||
(local $sum i32)
|
||||
(loop (result i32)
|
||||
@ -7,10 +7,11 @@
|
||||
(set_local $sum (i32.add (get_local $sum) (get_local $count)))
|
||||
(i32.sub (i32.const 1) (i32.eq
|
||||
(get_local $count)
|
||||
(i32.const 100000)
|
||||
(i32.const 50000)
|
||||
))
|
||||
(br_if 0)
|
||||
(get_local $sum)
|
||||
)
|
||||
(if (i32.ne (i32.const 1250025000)) (unreachable))
|
||||
)
|
||||
)
|
||||
|
40
install.sh
40
install.sh
@ -32,6 +32,7 @@ white="\033[37m"
|
||||
bold="\e[1m"
|
||||
dim="\e[2m"
|
||||
|
||||
# Warning: Remove this on the public repo
|
||||
RELEASES_URL="https://github.com/wasmerio/wasmer/releases"
|
||||
|
||||
wasmer_download_json() {
|
||||
@ -129,11 +130,11 @@ wasmer_detect_profile() {
|
||||
wasmer_link() {
|
||||
printf "$cyan> Adding to bash profile...$reset\n"
|
||||
WASMER_PROFILE="$(wasmer_detect_profile)"
|
||||
LOAD_STR="\n# Wasmer\nexport WASMER_DIR=\"\$HOME/.wasmer\"\n[ -s \"\$WASMER_DIR/wasmer.sh\" ] && source \"\$WASMER_DIR/wasmer.sh\" # This loads wasmer\n"
|
||||
SOURCE_STR="# Wasmer config\nexport WASMER_DIR=\"\$HOME/.wasmer\"\nexport WASMER_CACHE_DIR=\"\$WASMER_DIR/cache\"\nexport PATH=\"\$HOME/.wasmer/bin:\$PATH\"\n"
|
||||
LOAD_STR="\n# Wasmer\nexport WASMER_DIR=\"$INSTALL_DIRECTORY\"\n[ -s \"\$WASMER_DIR/wasmer.sh\" ] && source \"\$WASMER_DIR/wasmer.sh\" # This loads wasmer\n"
|
||||
SOURCE_STR="# Wasmer config\nexport WASMER_DIR=\"$INSTALL_DIRECTORY\"\nexport WASMER_CACHE_DIR=\"\$WASMER_DIR/cache\"\nexport PATH=\"\$WASMER_DIR/bin:\$WASMER_DIR/globals/wapm_packages/.bin:\$PATH\"\n"
|
||||
|
||||
# We create the wasmer.sh file
|
||||
printf "$SOURCE_STR" > "$HOME/.wasmer/wasmer.sh"
|
||||
printf "$SOURCE_STR" > "$INSTALL_DIRECTORY/wasmer.sh"
|
||||
|
||||
if [ -z "${WASMER_PROFILE-}" ] ; then
|
||||
printf "${red}Profile not found. Tried:\n* ${WASMER_PROFILE} (as defined in \$PROFILE)\n* ~/.bashrc\n* ~/.bash_profile\n* ~/.zshrc\n* ~/.profile.\n"
|
||||
@ -155,13 +156,15 @@ wasmer_link() {
|
||||
echo "If this isn't the profile of your current shell then please add the following to your correct profile:"
|
||||
printf "$LOAD_STR$reset\n"
|
||||
|
||||
version=`$HOME/.wasmer/bin/wasmer --version` || (
|
||||
version=`$INSTALL_DIRECTORY/bin/wasmer --version` || (
|
||||
printf "$red> wasmer was installed, but doesn't seem to be working :($reset\n"
|
||||
exit 1;
|
||||
)
|
||||
|
||||
printf "$green> Successfully installed $version!\n\n${reset}If you want to have the command available now please execute:\nsource $HOME/.wasmer/wasmer.sh$reset\n"
|
||||
printf "\nOtherwise, wasmer will be available the next time you open the terminal.\n"
|
||||
printf "$green> Successfully installed $version!\n\n${reset}If you want to have the command available now please execute:\nsource $INSTALL_DIRECTORY/wasmer.sh$reset\n"
|
||||
printf "\nOtherwise, wasmer and wapm will be available the next time you open the terminal.\n"
|
||||
echo "Note: during the alpha release of wapm, telemetry is enabled by default; if you would like to opt out, run \`wapm config set telemetry.enabled false\`."
|
||||
echo "If you notice anything wrong or have any issues, please file a bug at https://github.com/wasmerio/wapm-cli :)"
|
||||
fi
|
||||
}
|
||||
|
||||
@ -254,7 +257,7 @@ wasmer_install() {
|
||||
|
||||
"
|
||||
fi
|
||||
# if [ -d "$HOME/.wasmer" ]; then
|
||||
# if [ -d "$INSTALL_DIRECTORY" ]; then
|
||||
# if which wasmer; then
|
||||
# local latest_url
|
||||
# local specified_version
|
||||
@ -283,15 +286,14 @@ wasmer_install() {
|
||||
# exit 0
|
||||
# else
|
||||
# printf "$yellow> $wasmer_alt_version is already installed, Specified version: $specified_version.$reset\n"
|
||||
# rm -rf "$HOME/.wasmer"
|
||||
# rm -rf "$INSTALL_DIRECTORY"
|
||||
# fi
|
||||
# else
|
||||
# printf "$red> $HOME/.wasmer already exists, possibly from a past Wasmer install.$reset\n"
|
||||
# printf "$red> Remove it (rm -rf $HOME/.wasmer) and run this script again.$reset\n"
|
||||
# printf "$red> $INSTALL_DIRECTORY already exists, possibly from a past Wasmer install.$reset\n"
|
||||
# printf "$red> Remove it (rm -rf $INSTALL_DIRECTORY) and run this script again.$reset\n"
|
||||
# exit 0
|
||||
# fi
|
||||
# fi
|
||||
|
||||
wasmer_download # $1 $2
|
||||
wasmer_link
|
||||
wasmer_reset
|
||||
@ -369,12 +371,12 @@ wasmer_download() {
|
||||
WASMER=INSTALL_DIRECTORY
|
||||
|
||||
# assemble expected release artifact name
|
||||
BINARY="wasmer-${OS}-${ARCH}"
|
||||
BINARY="wasmer-${OS}-${ARCH}.tar.gz"
|
||||
|
||||
# add .exe if on windows
|
||||
if [ "$OS" = "windows" ]; then
|
||||
BINARY="$BINARY.exe"
|
||||
fi
|
||||
# if [ "$OS" = "windows" ]; then
|
||||
# BINARY="$BINARY.exe"
|
||||
# fi
|
||||
|
||||
# if WASMER_RELEASE_TAG was not provided, assume latest
|
||||
if [ -z "$WASMER_RELEASE_TAG" ]; then
|
||||
@ -417,9 +419,6 @@ wasmer_download() {
|
||||
printf "\033[K\n\033[1A"
|
||||
# printf "\033[1A$cyan> Downloaded$reset\033[K\n"
|
||||
# echo "Setting executable permissions."
|
||||
chmod +x "$DOWNLOAD_FILE"
|
||||
|
||||
INSTALL_NAME="wasmer"
|
||||
|
||||
# windows not supported yet
|
||||
# if [ "$OS" = "windows" ]; then
|
||||
@ -428,8 +427,9 @@ wasmer_download() {
|
||||
|
||||
# echo "Moving executable to $INSTALL_DIRECTORY/$INSTALL_NAME"
|
||||
|
||||
mkdir -p $INSTALL_DIRECTORY/bin
|
||||
mv "$DOWNLOAD_FILE" "$INSTALL_DIRECTORY/bin/$INSTALL_NAME"
|
||||
mkdir -p $INSTALL_DIRECTORY
|
||||
# Untar the wasmer contents in the install directory
|
||||
tar -C $INSTALL_DIRECTORY -zxvf $DOWNLOAD_FILE
|
||||
}
|
||||
|
||||
wasmer_verify_or_quit() {
|
||||
|
9
integration_tests/cowsay/README.md
Normal file
9
integration_tests/cowsay/README.md
Normal file
@ -0,0 +1,9 @@
|
||||
# `cowsay` integration test
|
||||
|
||||
|
||||
This starts Wasmer with the Cowsay WASI Wasm file. The test makes assertions on
|
||||
the output of Wasmer. Run test with:
|
||||
|
||||
```bash
|
||||
./integration_tests/cowsay/test.sh
|
||||
```
|
14
integration_tests/cowsay/test.sh
Executable file
14
integration_tests/cowsay/test.sh
Executable file
@ -0,0 +1,14 @@
|
||||
#! /bin/bash
|
||||
|
||||
nohup ./target/release/wasmer run examples/cowsay.wasm --disable-cache -- "hello integration"
|
||||
|
||||
if grep "hello integration" ./nohup.out
|
||||
then
|
||||
echo "cowsay wasi integration test succeeded"
|
||||
rm ./nohup.out
|
||||
exit 0
|
||||
else
|
||||
echo "cowsay wasi integration test failed"
|
||||
rm ./nohup.out
|
||||
exit -1
|
||||
fi
|
@ -22,7 +22,8 @@ The integration builds on the Wasmer runtime and allow us to run WebAssembly fil
|
||||
|
||||
Wasmer intends to support different integrations:
|
||||
|
||||
- [emscripten](./emscripten): run Emscripten-generated WebAssembly files, such as [Lua](../examples/lua.wasm) or [nginx](../examples/nginx/nginx.wasm).
|
||||
- [WASI](./wasi): run WebAssembly files with the [WASI ABI](https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/).
|
||||
- [Emscripten](./emscripten): run Emscripten-generated WebAssembly files, such as [Lua](../examples/lua.wasm) or [nginx](../examples/nginx/nginx.wasm).
|
||||
- Go ABI: _we will work on this soon! Want to give us a hand? ✋_
|
||||
- Blazor: _research period, see [tracking issue](https://github.com/wasmerio/wasmer/issues/97)_
|
||||
|
||||
@ -33,6 +34,6 @@ to tune the codegen properties (compile speed, performance, etc) to best fit the
|
||||
|
||||
Currently, we support multiple backends for compiling WebAssembly to machine code:
|
||||
|
||||
- [dynasm-backend](./dynasm-backend/): Dynasm backend - super fast compilation, slower runtime speed
|
||||
- [singlepass-backend](./singlepass-backend/): Single pass backend - super fast compilation, slower runtime speed
|
||||
- [clif-backend](./clif-backend/): Cranelift backend - slower compilation, normal runtime speed
|
||||
- [llvm-backend](./llvm-backend/): LLVM backend - slow compilation, native runtime speed
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-clif-backend"
|
||||
version = "0.2.0"
|
||||
version = "0.4.1"
|
||||
description = "Wasmer runtime Cranelift compiler backend"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
@ -8,14 +8,14 @@ repository = "https://github.com/wasmerio/wasmer"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.2.1" }
|
||||
cranelift-native = "0.26.0"
|
||||
cranelift-codegen = "0.26.0"
|
||||
cranelift-entity = "0.26.0"
|
||||
cranelift-wasm = "0.26.0"
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" }
|
||||
cranelift-native = "0.30.0"
|
||||
cranelift-codegen = "0.30.0"
|
||||
cranelift-entity = "0.30.0"
|
||||
cranelift-wasm = "0.30.0"
|
||||
hashbrown = "0.1"
|
||||
target-lexicon = "0.2.0"
|
||||
wasmparser = "0.23.0"
|
||||
target-lexicon = "0.3.0"
|
||||
wasmparser = "0.29.2"
|
||||
byteorder = "1"
|
||||
nix = "0.13.0"
|
||||
libc = "0.2.49"
|
||||
@ -33,7 +33,7 @@ version = "0.0.7"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] }
|
||||
wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.2.0" }
|
||||
wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.4.1" }
|
||||
|
||||
[features]
|
||||
debug = ["wasmer-runtime-core/debug"]
|
||||
|
31
lib/clif-backend/README.md
Normal file
31
lib/clif-backend/README.md
Normal file
@ -0,0 +1,31 @@
|
||||
<p align="center">
|
||||
<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">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://circleci.com/gh/wasmerio/wasmer/">
|
||||
<img src="https://img.shields.io/circleci/project/github/wasmerio/wasmer/master.svg" alt="Build Status">
|
||||
</a>
|
||||
<a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE">
|
||||
<img src="https://img.shields.io/github/license/wasmerio/wasmer.svg" alt="License">
|
||||
</a>
|
||||
<a href="https://spectrum.chat/wasmer">
|
||||
<img src="https://withspectrum.github.io/badge/badge.svg" alt="Join the Wasmer Community">
|
||||
</a>
|
||||
<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">
|
||||
</a>
|
||||
<a href="https://docs.rs/wasmer-clif-backend">
|
||||
<img src="https://docs.rs/wasmer-clif-backend/badge.svg" alt="Read our API documentation">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
# Wasmer Cranelift backend
|
||||
|
||||
Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully
|
||||
compatible with Emscripten, Rust and Go. [Learn
|
||||
more](https://github.com/wasmerio/wasmer).
|
||||
|
||||
This crate represents the Cranelift backend.
|
@ -5,7 +5,7 @@ use std::sync::Arc;
|
||||
use wasmer_runtime_core::{
|
||||
backend::{sys::Memory, CacheGen},
|
||||
cache::{Artifact, Error},
|
||||
module::{ModuleInfo, ModuleInner},
|
||||
module::ModuleInfo,
|
||||
structures::Map,
|
||||
types::{LocalFuncIndex, SigIndex},
|
||||
};
|
||||
@ -27,18 +27,12 @@ impl CacheGenerator {
|
||||
}
|
||||
|
||||
impl CacheGen for CacheGenerator {
|
||||
fn generate_cache(
|
||||
&self,
|
||||
module: &ModuleInner,
|
||||
) -> Result<(Box<ModuleInfo>, Box<[u8]>, Memory), Error> {
|
||||
let info = Box::new(module.info.clone());
|
||||
|
||||
fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), Error> {
|
||||
// Clone the memory to a new location. This could take a long time,
|
||||
// depending on the throughput of your memcpy implementation.
|
||||
let compiled_code = (*self.memory).clone();
|
||||
|
||||
Ok((
|
||||
info,
|
||||
self.backend_cache.into_backend_data()?.into_boxed_slice(),
|
||||
compiled_code,
|
||||
))
|
||||
|
@ -1,3 +1,5 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
mod cache;
|
||||
mod func_env;
|
||||
mod libcalls;
|
||||
|
@ -73,16 +73,15 @@ impl Module {
|
||||
handler_data.clone(),
|
||||
)?;
|
||||
|
||||
let protected_caller = Caller::new(&self.info, handler_data, trampolines);
|
||||
|
||||
let cache_gen = Box::new(CacheGenerator::new(
|
||||
backend_cache,
|
||||
Arc::clone(&func_resolver.memory),
|
||||
));
|
||||
|
||||
let runnable_module = Caller::new(handler_data, trampolines, func_resolver);
|
||||
|
||||
Ok(ModuleInner {
|
||||
func_resolver: Box::new(func_resolver),
|
||||
protected_caller: Box::new(protected_caller),
|
||||
runnable_module: Box::new(runnable_module),
|
||||
cache_gen,
|
||||
|
||||
info: self.info,
|
||||
@ -103,16 +102,15 @@ impl Module {
|
||||
)
|
||||
.map_err(|e| CacheError::Unknown(format!("{:?}", e)))?;
|
||||
|
||||
let protected_caller = Caller::new(&info, handler_data, trampolines);
|
||||
|
||||
let cache_gen = Box::new(CacheGenerator::new(
|
||||
backend_cache,
|
||||
Arc::clone(&func_resolver.memory),
|
||||
));
|
||||
|
||||
let runnable_module = Caller::new(handler_data, trampolines, func_resolver);
|
||||
|
||||
Ok(ModuleInner {
|
||||
func_resolver: Box::new(func_resolver),
|
||||
protected_caller: Box::new(protected_caller),
|
||||
runnable_module: Box::new(runnable_module),
|
||||
cache_gen,
|
||||
|
||||
info,
|
||||
@ -151,8 +149,8 @@ convert_clif_to_runtime_index![
|
||||
(SignatureIndex: SigIndex),
|
||||
];
|
||||
|
||||
impl<'a> From<Converter<&'a ir::Signature>> for FuncSig {
|
||||
fn from(signature: Converter<&'a ir::Signature>) -> Self {
|
||||
impl From<Converter<ir::Signature>> for FuncSig {
|
||||
fn from(signature: Converter<ir::Signature>) -> Self {
|
||||
FuncSig::new(
|
||||
signature
|
||||
.0
|
||||
|
@ -50,6 +50,20 @@ impl<'module, 'isa> ModuleEnv<'module, 'isa> {
|
||||
|
||||
Ok(self.func_bodies)
|
||||
}
|
||||
|
||||
/// Return the global for the given global index.
|
||||
pub fn get_global(&self, global_index: cranelift_wasm::GlobalIndex) -> &cranelift_wasm::Global {
|
||||
&self.globals[Converter(global_index).into()]
|
||||
}
|
||||
|
||||
/// Return the signature index for the given function index.
|
||||
pub fn get_func_type(
|
||||
&self,
|
||||
func_index: cranelift_wasm::FuncIndex,
|
||||
) -> cranelift_wasm::SignatureIndex {
|
||||
let sig_index: SigIndex = self.module.info.func_assoc[Converter(func_index).into()];
|
||||
Converter(sig_index).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> {
|
||||
@ -59,16 +73,11 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
}
|
||||
|
||||
/// Declares a function signature to the environment.
|
||||
fn declare_signature(&mut self, sig: &ir::Signature) {
|
||||
fn declare_signature(&mut self, sig: ir::Signature) {
|
||||
self.signatures.push(sig.clone());
|
||||
self.module.info.signatures.push(Converter(sig).into());
|
||||
}
|
||||
|
||||
/// Return the signature with the given index.
|
||||
fn get_signature(&self, clif_sig_index: cranelift_wasm::SignatureIndex) -> &ir::Signature {
|
||||
&self.signatures[Converter(clif_sig_index).into()]
|
||||
}
|
||||
|
||||
/// Declares a function import to the environment.
|
||||
fn declare_func_import(
|
||||
&mut self,
|
||||
@ -92,11 +101,6 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
});
|
||||
}
|
||||
|
||||
/// Return the number of imported funcs.
|
||||
fn get_num_func_imports(&self) -> usize {
|
||||
self.module.info.imported_functions.len()
|
||||
}
|
||||
|
||||
/// Declares the type (signature) of a local function in the module.
|
||||
fn declare_func_type(&mut self, clif_sig_index: cranelift_wasm::SignatureIndex) {
|
||||
// We convert the cranelift signature index to
|
||||
@ -106,15 +110,6 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
self.module.info.func_assoc.push(sig_index);
|
||||
}
|
||||
|
||||
/// Return the signature index for the given function index.
|
||||
fn get_func_type(
|
||||
&self,
|
||||
func_index: cranelift_wasm::FuncIndex,
|
||||
) -> cranelift_wasm::SignatureIndex {
|
||||
let sig_index: SigIndex = self.module.info.func_assoc[Converter(func_index).into()];
|
||||
Converter(sig_index).into()
|
||||
}
|
||||
|
||||
/// Declares a global to the environment.
|
||||
fn declare_global(&mut self, global: cranelift_wasm::Global) {
|
||||
let desc = GlobalDescriptor {
|
||||
@ -180,11 +175,6 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
self.globals.push(global);
|
||||
}
|
||||
|
||||
/// Return the global for the given global index.
|
||||
fn get_global(&self, global_index: cranelift_wasm::GlobalIndex) -> &cranelift_wasm::Global {
|
||||
&self.globals[Converter(global_index).into()]
|
||||
}
|
||||
|
||||
/// Declares a table to the environment.
|
||||
fn declare_table(&mut self, table: cranelift_wasm::Table) {
|
||||
use cranelift_wasm::TableElementType;
|
||||
@ -238,7 +228,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
table_index: cranelift_wasm::TableIndex,
|
||||
base: Option<cranelift_wasm::GlobalIndex>,
|
||||
offset: usize,
|
||||
elements: Vec<cranelift_wasm::FuncIndex>,
|
||||
elements: Box<[cranelift_wasm::FuncIndex]>,
|
||||
) {
|
||||
// Convert Cranelift GlobalIndex to wamser GlobalIndex
|
||||
// let base = base.map(|index| WasmerGlobalIndex::new(index.index()));
|
||||
@ -376,7 +366,11 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
}
|
||||
|
||||
/// Provides the contents of a function body.
|
||||
fn define_function_body(&mut self, body_bytes: &'data [u8]) -> cranelift_wasm::WasmResult<()> {
|
||||
fn define_function_body(
|
||||
&mut self,
|
||||
body_bytes: &'data [u8],
|
||||
body_offset: usize,
|
||||
) -> cranelift_wasm::WasmResult<()> {
|
||||
let mut func_translator = FuncTranslator::new();
|
||||
|
||||
let func_body = {
|
||||
@ -390,7 +384,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
|
||||
let mut func = ir::Function::with_name_signature(name, sig);
|
||||
|
||||
func_translator.translate(body_bytes, &mut func, &mut func_env)?;
|
||||
func_translator.translate(body_bytes, body_offset, &mut func, &mut func_env)?;
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
@ -530,7 +524,10 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
|
||||
.special_param(ir::ArgumentPurpose::VMContext)
|
||||
.expect("missing vmctx parameter");
|
||||
|
||||
let func_index = pos.ins().iconst(ir::types::I32, func_index.index() as i64);
|
||||
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]);
|
||||
|
||||
|
@ -224,6 +224,7 @@ pub enum TrapCode {
|
||||
IntegerDivisionByZero,
|
||||
BadConversionToInteger,
|
||||
Interrupt,
|
||||
UnreachableCodeReached,
|
||||
User(u16),
|
||||
}
|
||||
|
||||
@ -297,6 +298,7 @@ impl binemit::TrapSink for LocalTrapSink {
|
||||
ir::TrapCode::IntegerDivisionByZero => TrapCode::IntegerDivisionByZero,
|
||||
ir::TrapCode::BadConversionToInteger => TrapCode::BadConversionToInteger,
|
||||
ir::TrapCode::Interrupt => TrapCode::Interrupt,
|
||||
ir::TrapCode::UnreachableCodeReached => TrapCode::UnreachableCodeReached,
|
||||
ir::TrapCode::User(x) => TrapCode::User(x),
|
||||
};
|
||||
|
||||
|
@ -21,7 +21,6 @@ use wasmer_runtime_core::cache::Error as CacheError;
|
||||
use wasmer_runtime_core::{
|
||||
self,
|
||||
backend::{
|
||||
self,
|
||||
sys::{Memory, Protect},
|
||||
SigRegistry,
|
||||
},
|
||||
@ -357,13 +356,8 @@ pub struct FuncResolver {
|
||||
pub(crate) memory: Arc<Memory>,
|
||||
}
|
||||
|
||||
// Implements FuncResolver trait.
|
||||
impl backend::FuncResolver for FuncResolver {
|
||||
fn get(
|
||||
&self,
|
||||
_module: &wasmer_runtime_core::module::ModuleInner,
|
||||
index: LocalFuncIndex,
|
||||
) -> Option<NonNull<vm::Func>> {
|
||||
impl FuncResolver {
|
||||
pub fn lookup(&self, index: LocalFuncIndex) -> Option<NonNull<vm::Func>> {
|
||||
lookup_func(&self.map, &self.memory, index)
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,14 @@
|
||||
use crate::relocation::{TrapData, TrapSink};
|
||||
use crate::resolver::FuncResolver;
|
||||
use crate::trampoline::Trampolines;
|
||||
use hashbrown::HashSet;
|
||||
use libc::c_void;
|
||||
use std::{any::Any, cell::Cell, sync::Arc};
|
||||
use std::{any::Any, cell::Cell, ptr::NonNull, sync::Arc};
|
||||
use wasmer_runtime_core::{
|
||||
backend::{ProtectedCaller, Token, UserTrapper},
|
||||
error::RuntimeResult,
|
||||
export::Context,
|
||||
module::{ExportIndex, ModuleInfo, ModuleInner},
|
||||
types::{FuncIndex, FuncSig, LocalOrImport, SigIndex, Type, Value},
|
||||
vm::{self, ImportBacking},
|
||||
backend::RunnableModule,
|
||||
module::ModuleInfo,
|
||||
typed_func::{Wasm, WasmTrapInfo},
|
||||
types::{LocalFuncIndex, SigIndex},
|
||||
vm,
|
||||
};
|
||||
|
||||
#[cfg(unix)]
|
||||
@ -28,164 +27,89 @@ thread_local! {
|
||||
pub static TRAP_EARLY_DATA: Cell<Option<Box<dyn Any>>> = Cell::new(None);
|
||||
}
|
||||
|
||||
pub struct Trapper;
|
||||
|
||||
impl UserTrapper for Trapper {
|
||||
unsafe fn do_early_trap(&self, data: Box<dyn Any>) -> ! {
|
||||
TRAP_EARLY_DATA.with(|cell| cell.set(Some(data)));
|
||||
trigger_trap()
|
||||
}
|
||||
pub enum CallProtError {
|
||||
Trap(WasmTrapInfo),
|
||||
Error(Box<dyn Any>),
|
||||
}
|
||||
|
||||
pub struct Caller {
|
||||
func_export_set: HashSet<FuncIndex>,
|
||||
handler_data: HandlerData,
|
||||
trampolines: Arc<Trampolines>,
|
||||
resolver: FuncResolver,
|
||||
}
|
||||
|
||||
impl Caller {
|
||||
pub fn new(
|
||||
module: &ModuleInfo,
|
||||
handler_data: HandlerData,
|
||||
trampolines: Arc<Trampolines>,
|
||||
resolver: FuncResolver,
|
||||
) -> Self {
|
||||
let mut func_export_set = HashSet::new();
|
||||
for export_index in module.exports.values() {
|
||||
if let ExportIndex::Func(func_index) = export_index {
|
||||
func_export_set.insert(*func_index);
|
||||
}
|
||||
}
|
||||
if let Some(start_func_index) = module.start_func {
|
||||
func_export_set.insert(start_func_index);
|
||||
}
|
||||
|
||||
Self {
|
||||
func_export_set,
|
||||
handler_data,
|
||||
trampolines,
|
||||
resolver,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ProtectedCaller for Caller {
|
||||
fn call(
|
||||
&self,
|
||||
module: &ModuleInner,
|
||||
func_index: FuncIndex,
|
||||
params: &[Value],
|
||||
import_backing: &ImportBacking,
|
||||
vmctx: *mut vm::Ctx,
|
||||
_: Token,
|
||||
) -> RuntimeResult<Vec<Value>> {
|
||||
let (func_ptr, ctx, signature, sig_index) =
|
||||
get_func_from_index(&module, import_backing, func_index);
|
||||
impl RunnableModule for Caller {
|
||||
fn get_func(&self, _: &ModuleInfo, func_index: LocalFuncIndex) -> Option<NonNull<vm::Func>> {
|
||||
self.resolver.lookup(func_index)
|
||||
}
|
||||
|
||||
let vmctx_ptr = match ctx {
|
||||
Context::External(external_vmctx) => external_vmctx,
|
||||
Context::Internal => vmctx,
|
||||
};
|
||||
fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option<Wasm> {
|
||||
unsafe extern "C" fn invoke(
|
||||
trampoline: unsafe extern "C" fn(*mut vm::Ctx, NonNull<vm::Func>, *const u64, *mut u64),
|
||||
ctx: *mut vm::Ctx,
|
||||
func: NonNull<vm::Func>,
|
||||
args: *const u64,
|
||||
rets: *mut u64,
|
||||
trap_info: *mut WasmTrapInfo,
|
||||
user_error: *mut Option<Box<dyn Any>>,
|
||||
invoke_env: Option<NonNull<c_void>>,
|
||||
) -> bool {
|
||||
let handler_data = &*invoke_env.unwrap().cast().as_ptr();
|
||||
|
||||
assert!(self.func_export_set.contains(&func_index));
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let res = call_protected(handler_data, || {
|
||||
// Leap of faith.
|
||||
trampoline(ctx, func, args, rets);
|
||||
});
|
||||
|
||||
assert!(
|
||||
signature.returns().len() <= 1,
|
||||
"multi-value returns not yet supported"
|
||||
);
|
||||
// the trampoline is called from C on windows
|
||||
#[cfg(target_os = "windows")]
|
||||
let res = call_protected(handler_data, trampoline, ctx, func, args, rets);
|
||||
|
||||
assert!(
|
||||
signature.check_param_value_types(params),
|
||||
"incorrect signature"
|
||||
);
|
||||
|
||||
let param_vec: Vec<u64> = params
|
||||
.iter()
|
||||
.map(|val| match val {
|
||||
Value::I32(x) => *x as u64,
|
||||
Value::I64(x) => *x as u64,
|
||||
Value::F32(x) => x.to_bits() as u64,
|
||||
Value::F64(x) => x.to_bits(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut return_vec = vec![0; signature.returns().len()];
|
||||
match res {
|
||||
Err(err) => {
|
||||
match err {
|
||||
CallProtError::Trap(info) => *trap_info = info,
|
||||
CallProtError::Error(data) => *user_error = Some(data),
|
||||
}
|
||||
false
|
||||
}
|
||||
Ok(()) => true,
|
||||
}
|
||||
}
|
||||
|
||||
let trampoline = self
|
||||
.trampolines
|
||||
.lookup(sig_index)
|
||||
.expect("that trampoline doesn't exist");
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
call_protected(&self.handler_data, || unsafe {
|
||||
// Leap of faith.
|
||||
trampoline(
|
||||
vmctx_ptr,
|
||||
func_ptr,
|
||||
param_vec.as_ptr(),
|
||||
return_vec.as_mut_ptr(),
|
||||
);
|
||||
})?;
|
||||
|
||||
// the trampoline is called from C on windows
|
||||
#[cfg(target_os = "windows")]
|
||||
call_protected(
|
||||
&self.handler_data,
|
||||
Some(unsafe {
|
||||
Wasm::from_raw_parts(
|
||||
trampoline,
|
||||
vmctx_ptr,
|
||||
func_ptr,
|
||||
param_vec.as_ptr(),
|
||||
return_vec.as_mut_ptr(),
|
||||
)?;
|
||||
|
||||
Ok(return_vec
|
||||
.iter()
|
||||
.zip(signature.returns().iter())
|
||||
.map(|(&x, ty)| match ty {
|
||||
Type::I32 => Value::I32(x as i32),
|
||||
Type::I64 => Value::I64(x as i64),
|
||||
Type::F32 => Value::F32(f32::from_bits(x as u32)),
|
||||
Type::F64 => Value::F64(f64::from_bits(x as u64)),
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn get_early_trapper(&self) -> Box<dyn UserTrapper> {
|
||||
Box::new(Trapper)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_func_from_index<'a>(
|
||||
module: &'a ModuleInner,
|
||||
import_backing: &ImportBacking,
|
||||
func_index: FuncIndex,
|
||||
) -> (*const vm::Func, Context, &'a FuncSig, SigIndex) {
|
||||
let sig_index = *module
|
||||
.info
|
||||
.func_assoc
|
||||
.get(func_index)
|
||||
.expect("broken invariant, incorrect func index");
|
||||
|
||||
let (func_ptr, ctx) = match func_index.local_or_import(&module.info) {
|
||||
LocalOrImport::Local(local_func_index) => (
|
||||
module
|
||||
.func_resolver
|
||||
.get(&module, local_func_index)
|
||||
.expect("broken invariant, func resolver not synced with module.exports")
|
||||
.cast()
|
||||
.as_ptr() as *const _,
|
||||
Context::Internal,
|
||||
),
|
||||
LocalOrImport::Import(imported_func_index) => {
|
||||
let imported_func = import_backing.imported_func(imported_func_index);
|
||||
(
|
||||
imported_func.func as *const _,
|
||||
Context::External(imported_func.vmctx),
|
||||
invoke,
|
||||
Some(NonNull::from(&self.handler_data).cast()),
|
||||
)
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let signature = &module.info.signatures[sig_index];
|
||||
|
||||
(func_ptr, ctx, signature, sig_index)
|
||||
unsafe fn do_early_trap(&self, data: Box<dyn Any>) -> ! {
|
||||
TRAP_EARLY_DATA.with(|cell| cell.set(Some(data)));
|
||||
trigger_trap()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for HandlerData {}
|
||||
|
@ -1,5 +1,5 @@
|
||||
//! Installing signal handlers allows us to handle traps and out-of-bounds memory
|
||||
//! accesses that occur when runniing webassembly.
|
||||
//! accesses that occur when running WebAssembly.
|
||||
//!
|
||||
//! This code is inspired by: https://github.com/pepyakin/wasmtime/commit/625a2b6c0815b21996e111da51b9664feb174622
|
||||
//!
|
||||
@ -10,7 +10,7 @@
|
||||
//! unless you have memory unsafety elsewhere in your code.
|
||||
//!
|
||||
use crate::relocation::{TrapCode, TrapData};
|
||||
use crate::signal::HandlerData;
|
||||
use crate::signal::{CallProtError, HandlerData};
|
||||
use libc::{c_int, c_void, siginfo_t};
|
||||
use nix::sys::signal::{
|
||||
sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV,
|
||||
@ -18,7 +18,7 @@ use nix::sys::signal::{
|
||||
use std::cell::{Cell, UnsafeCell};
|
||||
use std::ptr;
|
||||
use std::sync::Once;
|
||||
use wasmer_runtime_core::error::{RuntimeError, RuntimeResult};
|
||||
use wasmer_runtime_core::typed_func::WasmTrapInfo;
|
||||
|
||||
extern "C" fn signal_trap_handler(
|
||||
signum: ::nix::libc::c_int,
|
||||
@ -62,7 +62,10 @@ pub unsafe fn trigger_trap() -> ! {
|
||||
longjmp(jmp_buf as *mut c_void, 0)
|
||||
}
|
||||
|
||||
pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> RuntimeResult<T> {
|
||||
pub fn call_protected<T>(
|
||||
handler_data: &HandlerData,
|
||||
f: impl FnOnce() -> T,
|
||||
) -> Result<T, CallProtError> {
|
||||
unsafe {
|
||||
let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get());
|
||||
let prev_jmp_buf = *jmp_buf;
|
||||
@ -76,7 +79,7 @@ pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R
|
||||
*jmp_buf = prev_jmp_buf;
|
||||
|
||||
if let Some(data) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) {
|
||||
Err(RuntimeError::Panic { data })
|
||||
Err(CallProtError::Error(data))
|
||||
} else {
|
||||
let (faulting_addr, inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get());
|
||||
|
||||
@ -85,33 +88,18 @@ pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R
|
||||
srcloc: _,
|
||||
}) = handler_data.lookup(inst_ptr)
|
||||
{
|
||||
Err(match Signal::from_c_int(signum) {
|
||||
Err(CallProtError::Trap(match Signal::from_c_int(signum) {
|
||||
Ok(SIGILL) => match trapcode {
|
||||
TrapCode::BadSignature => RuntimeError::Trap {
|
||||
msg: "incorrect call_indirect signature".into(),
|
||||
},
|
||||
TrapCode::IndirectCallToNull => RuntimeError::Trap {
|
||||
msg: "indirect call to null".into(),
|
||||
},
|
||||
TrapCode::HeapOutOfBounds => RuntimeError::Trap {
|
||||
msg: "memory out-of-bounds access".into(),
|
||||
},
|
||||
TrapCode::TableOutOfBounds => RuntimeError::Trap {
|
||||
msg: "table out-of-bounds access".into(),
|
||||
},
|
||||
_ => RuntimeError::Trap {
|
||||
msg: "unknown trap".into(),
|
||||
},
|
||||
},
|
||||
Ok(SIGSEGV) | Ok(SIGBUS) => RuntimeError::Trap {
|
||||
msg: "memory out-of-bounds access".into(),
|
||||
},
|
||||
Ok(SIGFPE) => RuntimeError::Trap {
|
||||
msg: "illegal arithmetic operation".into(),
|
||||
TrapCode::BadSignature => WasmTrapInfo::IncorrectCallIndirectSignature,
|
||||
TrapCode::IndirectCallToNull => WasmTrapInfo::CallIndirectOOB,
|
||||
TrapCode::HeapOutOfBounds => WasmTrapInfo::MemoryOutOfBounds,
|
||||
TrapCode::TableOutOfBounds => WasmTrapInfo::CallIndirectOOB,
|
||||
_ => WasmTrapInfo::Unknown,
|
||||
},
|
||||
Ok(SIGSEGV) | Ok(SIGBUS) => WasmTrapInfo::MemoryOutOfBounds,
|
||||
Ok(SIGFPE) => WasmTrapInfo::IllegalArithmetic,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
.into())
|
||||
}))
|
||||
} else {
|
||||
let signal = match Signal::from_c_int(signum) {
|
||||
Ok(SIGFPE) => "floating-point exception",
|
||||
@ -119,13 +107,11 @@ pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R
|
||||
Ok(SIGSEGV) => "segmentation violation",
|
||||
Ok(SIGBUS) => "bus error",
|
||||
Err(_) => "error while getting the Signal",
|
||||
_ => "unkown trapped signal",
|
||||
_ => "unknown trapped signal",
|
||||
};
|
||||
// When the trap-handler is fully implemented, this will return more information.
|
||||
Err(RuntimeError::Trap {
|
||||
msg: format!("unknown trap at {:p} - {}", faulting_addr, signal).into(),
|
||||
}
|
||||
.into())
|
||||
let s = format!("unknown trap at {:p} - {}", faulting_addr, signal);
|
||||
Err(CallProtError::Error(Box::new(s)))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -1,20 +1,24 @@
|
||||
use crate::relocation::{TrapCode, TrapData};
|
||||
use crate::signal::HandlerData;
|
||||
use crate::signal::{CallProtError, HandlerData};
|
||||
use crate::trampoline::Trampoline;
|
||||
use std::cell::Cell;
|
||||
use std::ffi::c_void;
|
||||
use std::ptr;
|
||||
use wasmer_runtime_core::error::{RuntimeError, RuntimeResult};
|
||||
use std::ptr::{self, NonNull};
|
||||
use wasmer_runtime_core::typed_func::WasmTrapInfo;
|
||||
use wasmer_runtime_core::vm::Ctx;
|
||||
use wasmer_runtime_core::vm::Func;
|
||||
use wasmer_win_exception_handler::CallProtectedData;
|
||||
pub use wasmer_win_exception_handler::_call_protected;
|
||||
use winapi::shared::minwindef::DWORD;
|
||||
use winapi::um::minwinbase::{
|
||||
EXCEPTION_ACCESS_VIOLATION, EXCEPTION_FLT_DENORMAL_OPERAND, EXCEPTION_FLT_DIVIDE_BY_ZERO,
|
||||
EXCEPTION_ACCESS_VIOLATION, EXCEPTION_ARRAY_BOUNDS_EXCEEDED, EXCEPTION_BREAKPOINT,
|
||||
EXCEPTION_DATATYPE_MISALIGNMENT, EXCEPTION_FLT_DENORMAL_OPERAND, EXCEPTION_FLT_DIVIDE_BY_ZERO,
|
||||
EXCEPTION_FLT_INEXACT_RESULT, EXCEPTION_FLT_INVALID_OPERATION, EXCEPTION_FLT_OVERFLOW,
|
||||
EXCEPTION_FLT_STACK_CHECK, EXCEPTION_FLT_UNDERFLOW, EXCEPTION_ILLEGAL_INSTRUCTION,
|
||||
EXCEPTION_INT_DIVIDE_BY_ZERO, EXCEPTION_INT_OVERFLOW, EXCEPTION_STACK_OVERFLOW,
|
||||
EXCEPTION_FLT_STACK_CHECK, EXCEPTION_FLT_UNDERFLOW, EXCEPTION_GUARD_PAGE,
|
||||
EXCEPTION_ILLEGAL_INSTRUCTION, EXCEPTION_INT_DIVIDE_BY_ZERO, EXCEPTION_INT_OVERFLOW,
|
||||
EXCEPTION_INVALID_HANDLE, EXCEPTION_IN_PAGE_ERROR, EXCEPTION_NONCONTINUABLE_EXCEPTION,
|
||||
EXCEPTION_POSSIBLE_DEADLOCK, EXCEPTION_PRIV_INSTRUCTION, EXCEPTION_SINGLE_STEP,
|
||||
EXCEPTION_STACK_OVERFLOW,
|
||||
};
|
||||
|
||||
thread_local! {
|
||||
@ -25,10 +29,10 @@ pub fn call_protected(
|
||||
handler_data: &HandlerData,
|
||||
trampoline: Trampoline,
|
||||
ctx: *mut Ctx,
|
||||
func: *const Func,
|
||||
func: NonNull<Func>,
|
||||
param_vec: *const u64,
|
||||
return_vec: *mut u64,
|
||||
) -> RuntimeResult<()> {
|
||||
) -> Result<(), CallProtError> {
|
||||
// TODO: trap early
|
||||
// user code error
|
||||
// if let Some(msg) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) {
|
||||
@ -42,7 +46,7 @@ pub fn call_protected(
|
||||
}
|
||||
|
||||
let CallProtectedData {
|
||||
code: signum,
|
||||
code,
|
||||
exception_address,
|
||||
instruction_pointer,
|
||||
} = result.unwrap_err();
|
||||
@ -52,40 +56,24 @@ pub fn call_protected(
|
||||
srcloc: _,
|
||||
}) = handler_data.lookup(instruction_pointer as _)
|
||||
{
|
||||
Err(match signum as DWORD {
|
||||
EXCEPTION_ACCESS_VIOLATION => RuntimeError::Trap {
|
||||
msg: "memory out-of-bounds access".into(),
|
||||
},
|
||||
Err(CallProtError::Trap(match code as DWORD {
|
||||
EXCEPTION_ACCESS_VIOLATION => WasmTrapInfo::MemoryOutOfBounds,
|
||||
EXCEPTION_ILLEGAL_INSTRUCTION => match trapcode {
|
||||
TrapCode::BadSignature => RuntimeError::Trap {
|
||||
msg: "incorrect call_indirect signature".into(),
|
||||
},
|
||||
TrapCode::IndirectCallToNull => RuntimeError::Trap {
|
||||
msg: "indirect call to null".into(),
|
||||
},
|
||||
TrapCode::HeapOutOfBounds => RuntimeError::Trap {
|
||||
msg: "memory out-of-bounds access".into(),
|
||||
},
|
||||
TrapCode::TableOutOfBounds => RuntimeError::Trap {
|
||||
msg: "table out-of-bounds access".into(),
|
||||
},
|
||||
_ => RuntimeError::Trap {
|
||||
msg: "unknown trap".into(),
|
||||
},
|
||||
},
|
||||
EXCEPTION_STACK_OVERFLOW => RuntimeError::Trap {
|
||||
msg: "stack overflow trap".into(),
|
||||
},
|
||||
EXCEPTION_INT_DIVIDE_BY_ZERO | EXCEPTION_INT_OVERFLOW => RuntimeError::Trap {
|
||||
msg: "illegal arithmetic operation".into(),
|
||||
},
|
||||
_ => RuntimeError::Trap {
|
||||
msg: "unknown trap".into(),
|
||||
TrapCode::BadSignature => WasmTrapInfo::IncorrectCallIndirectSignature,
|
||||
TrapCode::IndirectCallToNull => WasmTrapInfo::CallIndirectOOB,
|
||||
TrapCode::HeapOutOfBounds => WasmTrapInfo::MemoryOutOfBounds,
|
||||
TrapCode::TableOutOfBounds => WasmTrapInfo::CallIndirectOOB,
|
||||
TrapCode::UnreachableCodeReached => WasmTrapInfo::Unreachable,
|
||||
_ => WasmTrapInfo::Unknown,
|
||||
},
|
||||
EXCEPTION_STACK_OVERFLOW => WasmTrapInfo::Unknown,
|
||||
EXCEPTION_INT_DIVIDE_BY_ZERO | EXCEPTION_INT_OVERFLOW => {
|
||||
WasmTrapInfo::IllegalArithmetic
|
||||
}
|
||||
.into())
|
||||
_ => WasmTrapInfo::Unknown,
|
||||
}))
|
||||
} else {
|
||||
let signal = match signum as DWORD {
|
||||
let signal = match code as DWORD {
|
||||
EXCEPTION_FLT_DENORMAL_OPERAND
|
||||
| EXCEPTION_FLT_DIVIDE_BY_ZERO
|
||||
| EXCEPTION_FLT_INEXACT_RESULT
|
||||
@ -95,13 +83,28 @@ pub fn call_protected(
|
||||
| EXCEPTION_FLT_UNDERFLOW => "floating-point exception",
|
||||
EXCEPTION_ILLEGAL_INSTRUCTION => "illegal instruction",
|
||||
EXCEPTION_ACCESS_VIOLATION => "segmentation violation",
|
||||
_ => "unkown trapped signal",
|
||||
EXCEPTION_DATATYPE_MISALIGNMENT => "datatype misalignment",
|
||||
EXCEPTION_BREAKPOINT => "breakpoint",
|
||||
EXCEPTION_SINGLE_STEP => "single step",
|
||||
EXCEPTION_ARRAY_BOUNDS_EXCEEDED => "array bounds exceeded",
|
||||
EXCEPTION_INT_DIVIDE_BY_ZERO => "int div by zero",
|
||||
EXCEPTION_INT_OVERFLOW => "int overflow",
|
||||
EXCEPTION_PRIV_INSTRUCTION => "priv instruction",
|
||||
EXCEPTION_IN_PAGE_ERROR => "in page error",
|
||||
EXCEPTION_NONCONTINUABLE_EXCEPTION => "non continuable exception",
|
||||
EXCEPTION_STACK_OVERFLOW => "stack overflow",
|
||||
EXCEPTION_GUARD_PAGE => "guard page",
|
||||
EXCEPTION_INVALID_HANDLE => "invalid handle",
|
||||
EXCEPTION_POSSIBLE_DEADLOCK => "possible deadlock",
|
||||
_ => "unknown exception code",
|
||||
};
|
||||
|
||||
Err(RuntimeError::Trap {
|
||||
msg: format!("unknown trap at {} - {}", exception_address, signal).into(),
|
||||
}
|
||||
.into())
|
||||
let s = format!(
|
||||
"unhandled trap at {:x} - code #{:x}: {}",
|
||||
exception_address, code, signal,
|
||||
);
|
||||
|
||||
Err(CallProtError::Error(Box::new(s)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,8 +6,7 @@ use cranelift_codegen::{
|
||||
isa, Context,
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use std::ffi::c_void;
|
||||
use std::{iter, mem};
|
||||
use std::{iter, mem, ptr::NonNull};
|
||||
use wasmer_runtime_core::{
|
||||
backend::sys::{Memory, Protect},
|
||||
module::{ExportIndex, ModuleInfo},
|
||||
@ -23,8 +22,7 @@ impl RelocSink for NullRelocSink {
|
||||
fn reloc_jt(&mut self, _: u32, _: Reloc, _: ir::JumpTable) {}
|
||||
}
|
||||
|
||||
pub type Trampoline =
|
||||
unsafe extern "C" fn(*mut vm::Ctx, *const vm::Func, *const u64, *mut u64) -> c_void;
|
||||
pub type Trampoline = unsafe extern "C" fn(*mut vm::Ctx, NonNull<vm::Func>, *const u64, *mut u64);
|
||||
|
||||
pub struct Trampolines {
|
||||
memory: Memory,
|
||||
|
@ -1,33 +0,0 @@
|
||||
use wasmer_runtime_core::{
|
||||
backend::{FuncResolver, ProtectedCaller},
|
||||
module::ModuleInfo,
|
||||
structures::Map,
|
||||
types::{FuncIndex, FuncSig, SigIndex},
|
||||
};
|
||||
use wasmparser::{Operator, Type as WpType};
|
||||
|
||||
pub trait ModuleCodeGenerator<FCG: FunctionCodeGenerator, PC: ProtectedCaller, FR: FuncResolver> {
|
||||
fn check_precondition(&mut self, module_info: &ModuleInfo) -> Result<(), CodegenError>;
|
||||
fn next_function(&mut self) -> Result<&mut FCG, CodegenError>;
|
||||
fn finalize(self, module_info: &ModuleInfo) -> Result<(PC, FR), CodegenError>;
|
||||
fn feed_signatures(&mut self, signatures: Map<SigIndex, FuncSig>) -> Result<(), CodegenError>;
|
||||
fn feed_function_signatures(
|
||||
&mut self,
|
||||
assoc: Map<FuncIndex, SigIndex>,
|
||||
) -> Result<(), CodegenError>;
|
||||
fn feed_import_function(&mut self) -> Result<(), CodegenError>;
|
||||
}
|
||||
|
||||
pub trait FunctionCodeGenerator {
|
||||
fn feed_return(&mut self, ty: WpType) -> Result<(), CodegenError>;
|
||||
fn feed_param(&mut self, ty: WpType) -> Result<(), CodegenError>;
|
||||
fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), CodegenError>;
|
||||
fn begin_body(&mut self) -> Result<(), CodegenError>;
|
||||
fn feed_opcode(&mut self, op: Operator, module_info: &ModuleInfo) -> Result<(), CodegenError>;
|
||||
fn finalize(&mut self) -> Result<(), CodegenError>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CodegenError {
|
||||
pub message: &'static str,
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,92 +0,0 @@
|
||||
#![feature(proc_macro_hygiene)]
|
||||
|
||||
#[cfg(not(any(
|
||||
all(target_os = "macos", target_arch = "x86_64"),
|
||||
all(target_os = "linux", target_arch = "x86_64"),
|
||||
)))]
|
||||
compile_error!("This crate doesn't yet support compiling on operating systems other than linux and macos and architectures other than x86_64");
|
||||
|
||||
extern crate dynasmrt;
|
||||
|
||||
#[macro_use]
|
||||
extern crate dynasm;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
extern crate byteorder;
|
||||
|
||||
mod codegen;
|
||||
mod codegen_x64;
|
||||
mod parse;
|
||||
mod protect_unix;
|
||||
mod stack;
|
||||
|
||||
use crate::codegen::{CodegenError, ModuleCodeGenerator};
|
||||
use crate::parse::LoadError;
|
||||
use wasmer_runtime_core::{
|
||||
backend::{sys::Memory, Backend, CacheGen, Compiler, CompilerConfig, Token},
|
||||
cache::{Artifact, Error as CacheError},
|
||||
error::{CompileError, CompileResult},
|
||||
module::{ModuleInfo, ModuleInner},
|
||||
};
|
||||
|
||||
struct Placeholder;
|
||||
impl CacheGen for Placeholder {
|
||||
fn generate_cache(
|
||||
&self,
|
||||
_module: &ModuleInner,
|
||||
) -> Result<(Box<ModuleInfo>, Box<[u8]>, Memory), CacheError> {
|
||||
Err(CacheError::Unknown(
|
||||
"the dynasm backend doesn't support caching yet".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SinglePassCompiler {}
|
||||
impl SinglePassCompiler {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl Compiler for SinglePassCompiler {
|
||||
fn compile(
|
||||
&self,
|
||||
wasm: &[u8],
|
||||
compiler_config: CompilerConfig,
|
||||
_: Token,
|
||||
) -> CompileResult<ModuleInner> {
|
||||
let mut mcg = codegen_x64::X64ModuleCodeGenerator::new();
|
||||
let info = parse::read_module(wasm, Backend::Dynasm, &mut mcg, &compiler_config)?;
|
||||
let (ec, resolver) = mcg.finalize(&info)?;
|
||||
Ok(ModuleInner {
|
||||
cache_gen: Box::new(Placeholder),
|
||||
func_resolver: Box::new(resolver),
|
||||
protected_caller: Box::new(ec),
|
||||
info: info,
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn from_cache(&self, _artifact: Artifact, _: Token) -> Result<ModuleInner, CacheError> {
|
||||
Err(CacheError::Unknown(
|
||||
"the dynasm backend doesn't support caching yet".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CodegenError> for CompileError {
|
||||
fn from(other: CodegenError) -> CompileError {
|
||||
CompileError::InternalError {
|
||||
msg: other.message.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LoadError> for CompileError {
|
||||
fn from(other: LoadError) -> CompileError {
|
||||
CompileError::InternalError {
|
||||
msg: format!("{:?}", other),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,447 +0,0 @@
|
||||
use crate::codegen::{CodegenError, FunctionCodeGenerator, ModuleCodeGenerator};
|
||||
use hashbrown::HashMap;
|
||||
use wasmer_runtime_core::{
|
||||
backend::{Backend, CompilerConfig, FuncResolver, ProtectedCaller},
|
||||
module::{
|
||||
DataInitializer, ExportIndex, ImportName, ModuleInfo, StringTable, StringTableBuilder,
|
||||
TableInitializer,
|
||||
},
|
||||
structures::{Map, TypedIndex},
|
||||
types::{
|
||||
ElementType, FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit,
|
||||
ImportedGlobalIndex, Initializer, MemoryDescriptor, MemoryIndex, SigIndex, TableDescriptor,
|
||||
TableIndex, Type, Value,
|
||||
},
|
||||
units::Pages,
|
||||
};
|
||||
use wasmparser::{
|
||||
BinaryReaderError, Data, DataKind, Element, ElementKind, Export, ExternalKind, FuncType,
|
||||
Import, ImportSectionEntryType, InitExpr, ModuleReader, Operator, SectionCode, Type as WpType,
|
||||
WasmDecoder,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum LoadError {
|
||||
Parse(BinaryReaderError),
|
||||
Codegen(CodegenError),
|
||||
}
|
||||
|
||||
impl From<BinaryReaderError> for LoadError {
|
||||
fn from(other: BinaryReaderError) -> LoadError {
|
||||
LoadError::Parse(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CodegenError> for LoadError {
|
||||
fn from(other: CodegenError) -> LoadError {
|
||||
LoadError::Codegen(other)
|
||||
}
|
||||
}
|
||||
|
||||
fn validate(bytes: &[u8]) -> Result<(), LoadError> {
|
||||
let mut parser = wasmparser::ValidatingParser::new(
|
||||
bytes,
|
||||
Some(wasmparser::ValidatingParserConfig {
|
||||
operator_config: wasmparser::OperatorValidatorConfig {
|
||||
enable_threads: false,
|
||||
enable_reference_types: false,
|
||||
enable_simd: false,
|
||||
enable_bulk_memory: false,
|
||||
},
|
||||
mutable_global_imports: false,
|
||||
}),
|
||||
);
|
||||
|
||||
loop {
|
||||
let state = parser.read();
|
||||
match *state {
|
||||
wasmparser::ParserState::EndWasm => break Ok(()),
|
||||
wasmparser::ParserState::Error(err) => Err(LoadError::Parse(err))?,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_module<
|
||||
MCG: ModuleCodeGenerator<FCG, PC, FR>,
|
||||
FCG: FunctionCodeGenerator,
|
||||
PC: ProtectedCaller,
|
||||
FR: FuncResolver,
|
||||
>(
|
||||
wasm: &[u8],
|
||||
backend: Backend,
|
||||
mcg: &mut MCG,
|
||||
compiler_config: &CompilerConfig,
|
||||
) -> Result<ModuleInfo, LoadError> {
|
||||
validate(wasm)?;
|
||||
let mut info = ModuleInfo {
|
||||
memories: Map::new(),
|
||||
globals: Map::new(),
|
||||
tables: Map::new(),
|
||||
|
||||
imported_functions: Map::new(),
|
||||
imported_memories: Map::new(),
|
||||
imported_tables: Map::new(),
|
||||
imported_globals: Map::new(),
|
||||
|
||||
exports: Default::default(),
|
||||
|
||||
data_initializers: Vec::new(),
|
||||
elem_initializers: Vec::new(),
|
||||
|
||||
start_func: None,
|
||||
|
||||
func_assoc: Map::new(),
|
||||
signatures: Map::new(),
|
||||
backend: backend,
|
||||
|
||||
namespace_table: StringTable::new(),
|
||||
name_table: StringTable::new(),
|
||||
|
||||
em_symbol_map: compiler_config.symbol_map.clone(),
|
||||
|
||||
custom_sections: HashMap::new(),
|
||||
};
|
||||
|
||||
let mut reader = ModuleReader::new(wasm)?;
|
||||
|
||||
loop {
|
||||
if reader.eof() {
|
||||
return Ok(info);
|
||||
}
|
||||
|
||||
let section = reader.read()?;
|
||||
|
||||
match section.code {
|
||||
SectionCode::Type => {
|
||||
let type_reader = section.get_type_section_reader()?;
|
||||
|
||||
for ty in type_reader {
|
||||
let ty = ty?;
|
||||
info.signatures.push(func_type_to_func_sig(ty)?);
|
||||
}
|
||||
|
||||
mcg.feed_signatures(info.signatures.clone())?;
|
||||
}
|
||||
SectionCode::Import => {
|
||||
let import_reader = section.get_import_section_reader()?;
|
||||
let mut namespace_builder = StringTableBuilder::new();
|
||||
let mut name_builder = StringTableBuilder::new();
|
||||
|
||||
for import in import_reader {
|
||||
let Import { module, field, ty } = import?;
|
||||
|
||||
let namespace_index = namespace_builder.register(module);
|
||||
let name_index = name_builder.register(field);
|
||||
let import_name = ImportName {
|
||||
namespace_index,
|
||||
name_index,
|
||||
};
|
||||
|
||||
match ty {
|
||||
ImportSectionEntryType::Function(sigindex) => {
|
||||
let sigindex = SigIndex::new(sigindex as usize);
|
||||
info.imported_functions.push(import_name);
|
||||
info.func_assoc.push(sigindex);
|
||||
mcg.feed_import_function()?;
|
||||
}
|
||||
ImportSectionEntryType::Table(table_ty) => {
|
||||
assert_eq!(table_ty.element_type, WpType::AnyFunc);
|
||||
let table_desc = TableDescriptor {
|
||||
element: ElementType::Anyfunc,
|
||||
minimum: table_ty.limits.initial,
|
||||
maximum: table_ty.limits.maximum,
|
||||
};
|
||||
|
||||
info.imported_tables.push((import_name, table_desc));
|
||||
}
|
||||
ImportSectionEntryType::Memory(memory_ty) => {
|
||||
let mem_desc = MemoryDescriptor {
|
||||
minimum: Pages(memory_ty.limits.initial),
|
||||
maximum: memory_ty.limits.maximum.map(|max| Pages(max)),
|
||||
shared: memory_ty.shared,
|
||||
};
|
||||
info.imported_memories.push((import_name, mem_desc));
|
||||
}
|
||||
ImportSectionEntryType::Global(global_ty) => {
|
||||
let global_desc = GlobalDescriptor {
|
||||
mutable: global_ty.mutable,
|
||||
ty: wp_type_to_type(global_ty.content_type)?,
|
||||
};
|
||||
info.imported_globals.push((import_name, global_desc));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info.namespace_table = namespace_builder.finish();
|
||||
info.name_table = name_builder.finish();
|
||||
}
|
||||
SectionCode::Function => {
|
||||
let func_decl_reader = section.get_function_section_reader()?;
|
||||
|
||||
for sigindex in func_decl_reader {
|
||||
let sigindex = sigindex?;
|
||||
|
||||
let sigindex = SigIndex::new(sigindex as usize);
|
||||
info.func_assoc.push(sigindex);
|
||||
}
|
||||
|
||||
mcg.feed_function_signatures(info.func_assoc.clone())?;
|
||||
}
|
||||
SectionCode::Table => {
|
||||
let table_decl_reader = section.get_table_section_reader()?;
|
||||
|
||||
for table_ty in table_decl_reader {
|
||||
let table_ty = table_ty?;
|
||||
|
||||
let table_desc = TableDescriptor {
|
||||
element: ElementType::Anyfunc,
|
||||
minimum: table_ty.limits.initial,
|
||||
maximum: table_ty.limits.maximum,
|
||||
};
|
||||
|
||||
info.tables.push(table_desc);
|
||||
}
|
||||
}
|
||||
SectionCode::Memory => {
|
||||
let mem_decl_reader = section.get_memory_section_reader()?;
|
||||
|
||||
for memory_ty in mem_decl_reader {
|
||||
let memory_ty = memory_ty?;
|
||||
|
||||
let mem_desc = MemoryDescriptor {
|
||||
minimum: Pages(memory_ty.limits.initial),
|
||||
maximum: memory_ty.limits.maximum.map(|max| Pages(max)),
|
||||
shared: memory_ty.shared,
|
||||
};
|
||||
|
||||
info.memories.push(mem_desc);
|
||||
}
|
||||
}
|
||||
SectionCode::Global => {
|
||||
let global_decl_reader = section.get_global_section_reader()?;
|
||||
|
||||
for global in global_decl_reader {
|
||||
let global = global?;
|
||||
|
||||
let desc = GlobalDescriptor {
|
||||
mutable: global.ty.mutable,
|
||||
ty: wp_type_to_type(global.ty.content_type)?,
|
||||
};
|
||||
|
||||
let global_init = GlobalInit {
|
||||
desc,
|
||||
init: eval_init_expr(&global.init_expr)?,
|
||||
};
|
||||
|
||||
info.globals.push(global_init);
|
||||
}
|
||||
}
|
||||
SectionCode::Export => {
|
||||
let export_reader = section.get_export_section_reader()?;
|
||||
|
||||
for export in export_reader {
|
||||
let Export { field, kind, index } = export?;
|
||||
|
||||
let export_index = match kind {
|
||||
ExternalKind::Function => ExportIndex::Func(FuncIndex::new(index as usize)),
|
||||
ExternalKind::Table => ExportIndex::Table(TableIndex::new(index as usize)),
|
||||
ExternalKind::Memory => {
|
||||
ExportIndex::Memory(MemoryIndex::new(index as usize))
|
||||
}
|
||||
ExternalKind::Global => {
|
||||
ExportIndex::Global(GlobalIndex::new(index as usize))
|
||||
}
|
||||
};
|
||||
|
||||
info.exports.insert(field.to_string(), export_index);
|
||||
}
|
||||
}
|
||||
SectionCode::Start => {
|
||||
let start_index = section.get_start_section_content()?;
|
||||
|
||||
info.start_func = Some(FuncIndex::new(start_index as usize));
|
||||
}
|
||||
SectionCode::Element => {
|
||||
let element_reader = section.get_element_section_reader()?;
|
||||
|
||||
for element in element_reader {
|
||||
let Element { kind, items } = element?;
|
||||
|
||||
match kind {
|
||||
ElementKind::Active {
|
||||
table_index,
|
||||
init_expr,
|
||||
} => {
|
||||
let table_index = TableIndex::new(table_index as usize);
|
||||
let base = eval_init_expr(&init_expr)?;
|
||||
let items_reader = items.get_items_reader()?;
|
||||
|
||||
let elements: Vec<_> = items_reader
|
||||
.into_iter()
|
||||
.map(|res| res.map(|index| FuncIndex::new(index as usize)))
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
let table_init = TableInitializer {
|
||||
table_index,
|
||||
base,
|
||||
elements,
|
||||
};
|
||||
|
||||
info.elem_initializers.push(table_init);
|
||||
}
|
||||
ElementKind::Passive(_ty) => {
|
||||
return Err(BinaryReaderError {
|
||||
message: "passive tables are not yet supported",
|
||||
offset: -1isize as usize,
|
||||
}
|
||||
.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SectionCode::Code => {
|
||||
let mut code_reader = section.get_code_section_reader()?;
|
||||
if code_reader.get_count() as usize > info.func_assoc.len() {
|
||||
return Err(BinaryReaderError {
|
||||
message: "code_reader.get_count() > info.func_assoc.len()",
|
||||
offset: ::std::usize::MAX,
|
||||
}
|
||||
.into());
|
||||
}
|
||||
mcg.check_precondition(&info)?;
|
||||
for i in 0..code_reader.get_count() {
|
||||
let item = code_reader.read()?;
|
||||
let fcg = mcg.next_function()?;
|
||||
let sig = info
|
||||
.signatures
|
||||
.get(
|
||||
*info
|
||||
.func_assoc
|
||||
.get(FuncIndex::new(i as usize + info.imported_functions.len()))
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
for ret in sig.returns() {
|
||||
fcg.feed_return(type_to_wp_type(*ret))?;
|
||||
}
|
||||
for param in sig.params() {
|
||||
fcg.feed_param(type_to_wp_type(*param))?;
|
||||
}
|
||||
for local in item.get_locals_reader()? {
|
||||
let (count, ty) = local?;
|
||||
fcg.feed_local(ty, count as usize)?;
|
||||
}
|
||||
fcg.begin_body()?;
|
||||
for op in item.get_operators_reader()? {
|
||||
let op = op?;
|
||||
fcg.feed_opcode(op, &info)?;
|
||||
}
|
||||
fcg.finalize()?;
|
||||
}
|
||||
}
|
||||
SectionCode::Data => {
|
||||
let data_reader = section.get_data_section_reader()?;
|
||||
|
||||
for data in data_reader {
|
||||
let Data { kind, data } = data?;
|
||||
|
||||
match kind {
|
||||
DataKind::Active {
|
||||
memory_index,
|
||||
init_expr,
|
||||
} => {
|
||||
let memory_index = MemoryIndex::new(memory_index as usize);
|
||||
let base = eval_init_expr(&init_expr)?;
|
||||
|
||||
let data_init = DataInitializer {
|
||||
memory_index,
|
||||
base,
|
||||
data: data.to_vec(),
|
||||
};
|
||||
|
||||
info.data_initializers.push(data_init);
|
||||
}
|
||||
DataKind::Passive => {
|
||||
return Err(BinaryReaderError {
|
||||
message: "passive memories are not yet supported",
|
||||
offset: -1isize as usize,
|
||||
}
|
||||
.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SectionCode::DataCount => {}
|
||||
SectionCode::Custom { .. } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wp_type_to_type(ty: WpType) -> Result<Type, BinaryReaderError> {
|
||||
Ok(match ty {
|
||||
WpType::I32 => Type::I32,
|
||||
WpType::I64 => Type::I64,
|
||||
WpType::F32 => Type::F32,
|
||||
WpType::F64 => Type::F64,
|
||||
WpType::V128 => {
|
||||
return Err(BinaryReaderError {
|
||||
message: "the wasmer llvm backend does not yet support the simd extension",
|
||||
offset: -1isize as usize,
|
||||
});
|
||||
}
|
||||
_ => panic!("broken invariant, invalid type"),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn type_to_wp_type(ty: Type) -> WpType {
|
||||
match ty {
|
||||
Type::I32 => WpType::I32,
|
||||
Type::I64 => WpType::I64,
|
||||
Type::F32 => WpType::F32,
|
||||
Type::F64 => WpType::F64,
|
||||
}
|
||||
}
|
||||
|
||||
fn func_type_to_func_sig(func_ty: FuncType) -> Result<FuncSig, BinaryReaderError> {
|
||||
assert_eq!(func_ty.form, WpType::Func);
|
||||
|
||||
Ok(FuncSig::new(
|
||||
func_ty
|
||||
.params
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(wp_type_to_type)
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
func_ty
|
||||
.returns
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(wp_type_to_type)
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
))
|
||||
}
|
||||
|
||||
fn eval_init_expr(expr: &InitExpr) -> Result<Initializer, BinaryReaderError> {
|
||||
let mut reader = expr.get_operators_reader();
|
||||
let (op, offset) = reader.read_with_offset()?;
|
||||
Ok(match op {
|
||||
Operator::GetGlobal { global_index } => {
|
||||
Initializer::GetGlobal(ImportedGlobalIndex::new(global_index as usize))
|
||||
}
|
||||
Operator::I32Const { value } => Initializer::Const(Value::I32(value)),
|
||||
Operator::I64Const { value } => Initializer::Const(Value::I64(value)),
|
||||
Operator::F32Const { value } => {
|
||||
Initializer::Const(Value::F32(f32::from_bits(value.bits())))
|
||||
}
|
||||
Operator::F64Const { value } => {
|
||||
Initializer::Const(Value::F64(f64::from_bits(value.bits())))
|
||||
}
|
||||
_ => {
|
||||
return Err(BinaryReaderError {
|
||||
message: "init expr evaluation failed: unsupported opcode",
|
||||
offset,
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
@ -1,164 +0,0 @@
|
||||
use crate::codegen::CodegenError;
|
||||
use dynasmrt::DynamicLabel;
|
||||
use wasmparser::Type as WpType;
|
||||
|
||||
/*#[repr(u8)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum RegisterName {
|
||||
RDI,
|
||||
RSI,
|
||||
RDX,
|
||||
RCX,
|
||||
R8,
|
||||
R9,
|
||||
R10,
|
||||
R11,
|
||||
RBX,
|
||||
R12,
|
||||
R13,
|
||||
R14,
|
||||
R15,
|
||||
Invalid,
|
||||
}*/
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum IfElseState {
|
||||
None,
|
||||
If(DynamicLabel),
|
||||
Else,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ControlFrame {
|
||||
pub label: DynamicLabel,
|
||||
pub loop_like: bool,
|
||||
pub if_else: IfElseState,
|
||||
pub returns: Vec<WpType>,
|
||||
pub value_stack_depth_before: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ControlStack {
|
||||
pub frames: Vec<ControlFrame>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ValueStack {
|
||||
pub num_regs: u8,
|
||||
pub values: Vec<ValueInfo>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct ValueInfo {
|
||||
pub ty: WpType,
|
||||
pub location: ValueLocation,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum ValueLocation {
|
||||
Register(ScratchRegister),
|
||||
Stack,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct ScratchRegister(u8);
|
||||
|
||||
impl ScratchRegister {
|
||||
pub fn raw_id(&self) -> u8 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueLocation {
|
||||
pub fn is_register(&self) -> bool {
|
||||
if let ValueLocation::Register(_) = *self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_register(&self) -> Result<ScratchRegister, CodegenError> {
|
||||
if let ValueLocation::Register(id) = *self {
|
||||
Ok(id)
|
||||
} else {
|
||||
Err(CodegenError {
|
||||
message: "not a register location",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueStack {
|
||||
pub fn new(num_regs: u8) -> ValueStack {
|
||||
ValueStack {
|
||||
num_regs: num_regs,
|
||||
values: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn next_location(&self, loc: &ValueLocation) -> ValueLocation {
|
||||
match *loc {
|
||||
ValueLocation::Register(ScratchRegister(x)) => {
|
||||
if x >= self.num_regs - 1 {
|
||||
ValueLocation::Stack
|
||||
} else {
|
||||
ValueLocation::Register(ScratchRegister(x + 1))
|
||||
}
|
||||
}
|
||||
ValueLocation::Stack => ValueLocation::Stack,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, ty: WpType) -> ValueLocation {
|
||||
let loc = self
|
||||
.values
|
||||
.last()
|
||||
.map(|x| self.next_location(&x.location))
|
||||
.unwrap_or(ValueLocation::Register(ScratchRegister(0)));
|
||||
self.values.push(ValueInfo {
|
||||
ty: ty,
|
||||
location: loc,
|
||||
});
|
||||
loc
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) -> Result<ValueInfo, CodegenError> {
|
||||
match self.values.pop() {
|
||||
Some(x) => Ok(x),
|
||||
None => Err(CodegenError {
|
||||
message: "no value on top of stack",
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop2(&mut self) -> Result<(ValueInfo, ValueInfo), CodegenError> {
|
||||
if self.values.len() < 2 {
|
||||
Err(CodegenError {
|
||||
message: "less than 2 values on top of stack",
|
||||
})
|
||||
} else {
|
||||
let v2 = self.values.pop().unwrap();
|
||||
let v1 = self.values.pop().unwrap();
|
||||
Ok((v1, v2))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_depth(&mut self, target_depth: usize) {
|
||||
self.values.truncate(target_depth);
|
||||
}
|
||||
}
|
||||
|
||||
impl ControlStack {
|
||||
pub fn new(label: DynamicLabel, returns: Vec<WpType>) -> ControlStack {
|
||||
ControlStack {
|
||||
frames: vec![ControlFrame {
|
||||
label: label,
|
||||
loop_like: false,
|
||||
if_else: IfElseState::None,
|
||||
returns: returns,
|
||||
value_stack_depth_before: 0,
|
||||
}],
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-emscripten"
|
||||
version = "0.2.1"
|
||||
version = "0.4.1"
|
||||
description = "Wasmer runtime emscripten implementation library"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
@ -9,14 +9,14 @@ edition = "2018"
|
||||
build = "build/mod.rs"
|
||||
|
||||
[dependencies]
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.2.1" }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" }
|
||||
lazy_static = "1.2.0"
|
||||
libc = "0.2.49"
|
||||
byteorder = "1"
|
||||
time = "0.1.41"
|
||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.2.0" }
|
||||
wasmer-dynasm-backend = { path = "../dynasm-backend", version = "0.1.0", optional = true }
|
||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.1.0", optional = true }
|
||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.4.1" }
|
||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.1", optional = true }
|
||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.4.1", optional = true }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
rand = "0.6"
|
||||
@ -30,4 +30,5 @@ glob = "0.2.11"
|
||||
[features]
|
||||
clif = []
|
||||
llvm = ["wasmer-llvm-backend"]
|
||||
dynasm = ["wasmer-dynasm-backend"]
|
||||
singlepass = ["wasmer-singlepass-backend"]
|
||||
debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"]
|
||||
|
@ -46,6 +46,21 @@ pub fn compile(file: &str, ignores: &Vec<String>) -> Option<String> {
|
||||
output_path.set_extension("js");
|
||||
let output_str = output_path.to_str().unwrap();
|
||||
|
||||
let wasm_file_metadata = {
|
||||
let mut wasm_file_path = PathBuf::from(file);
|
||||
wasm_file_path.set_extension("wasm");
|
||||
if let Ok(wasm_file) = File::open(wasm_file_path) {
|
||||
Some(wasm_file.metadata().unwrap())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let real_file = File::open(file).unwrap();
|
||||
let file_metadata = real_file.metadata().unwrap();
|
||||
if wasm_file_metadata.is_none()
|
||||
|| file_metadata.modified().unwrap() >= wasm_file_metadata.unwrap().modified().unwrap()
|
||||
{
|
||||
// Compile to wasm
|
||||
let _wasm_compilation = Command::new("emcc")
|
||||
.arg(file)
|
||||
@ -67,6 +82,7 @@ pub fn compile(file: &str, ignores: &Vec<String>) -> Option<String> {
|
||||
} else {
|
||||
println!("Output JS not found: {}", output_str);
|
||||
}
|
||||
}
|
||||
|
||||
let mut output_path = PathBuf::from(file);
|
||||
output_path.set_extension("output");
|
||||
|
4
lib/emscripten/emtests/hello.cpp
vendored
Normal file
4
lib/emscripten/emtests/hello.cpp
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
#include <iostream>
|
||||
int main() {
|
||||
std::cout << "hello world\n";
|
||||
}
|
2
lib/emscripten/emtests/hello.out
vendored
Normal file
2
lib/emscripten/emtests/hello.out
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
hello world
|
||||
|
BIN
lib/emscripten/emtests/hello.wasm
vendored
Normal file
BIN
lib/emscripten/emtests/hello.wasm
vendored
Normal file
Binary file not shown.
52
lib/emscripten/emtests/ignores.txt
vendored
52
lib/emscripten/emtests/ignores.txt
vendored
@ -30,16 +30,7 @@ test_i64
|
||||
test_i64_7z
|
||||
test_i64_varargs
|
||||
test_llvm_intrinsics
|
||||
test_longjmp2
|
||||
test_longjmp3
|
||||
test_longjmp4
|
||||
test_longjmp
|
||||
test_longjmp_exc
|
||||
test_longjmp_funcptr
|
||||
test_longjmp_repeat
|
||||
test_longjmp_stacked
|
||||
test_longjmp_throw
|
||||
test_longjmp_unwind
|
||||
test_lower_intrinsics
|
||||
test_main_thread_async_em_asm
|
||||
test_mainenv
|
||||
@ -72,3 +63,46 @@ test_std_cout_new
|
||||
test_strptime_reentrant
|
||||
test_gmtime
|
||||
test_time_c
|
||||
test_execvp
|
||||
test_nl_types
|
||||
test_phiundef
|
||||
test_pipe
|
||||
test_printf_2
|
||||
test_printf_more
|
||||
test_regex
|
||||
test_relocatable_void_function
|
||||
test_rounding
|
||||
test_set_align
|
||||
test_sintvars
|
||||
test_sizeof
|
||||
test_sscanf
|
||||
test_sscanf_3
|
||||
test_sscanf_4
|
||||
test_sscanf_5
|
||||
test_sscanf_6
|
||||
test_sscanf_caps
|
||||
test_sscanf_float
|
||||
test_sscanf_n
|
||||
test_strcasecmp
|
||||
test_strcmp_uni
|
||||
test_strndup
|
||||
test_strstr
|
||||
test_strtod
|
||||
test_strtok
|
||||
test_strtol_bin
|
||||
test_strtol_dec
|
||||
test_strtol_hex
|
||||
test_strtol_oct
|
||||
test_strtoll_bin
|
||||
test_strtoll_dec
|
||||
test_strtoll_hex
|
||||
test_strtoll_oct
|
||||
test_struct_varargs
|
||||
test_transtrcase
|
||||
test_trickystring
|
||||
test_unary_literal
|
||||
test_vfs
|
||||
test_vprintf
|
||||
test_vsnprintf
|
||||
test_write_stdout_fileno
|
||||
test_zerodiv
|
||||
|
@ -1,8 +1,9 @@
|
||||
use crate::emscripten_target;
|
||||
use wasmer_runtime_core::vm::Ctx;
|
||||
|
||||
///emscripten: _llvm_bswap_i64
|
||||
pub fn _llvm_bswap_i64(_ctx: &mut Ctx, _low: i32, high: i32) -> i32 {
|
||||
debug!("emscripten::_llvm_bswap_i64");
|
||||
// setTempRet0(low.swap_bytes)
|
||||
emscripten_target::setTempRet0(_ctx, _low.swap_bytes());
|
||||
high.swap_bytes()
|
||||
}
|
||||
|
@ -5,85 +5,25 @@ use crate::env::get_emscripten_data;
|
||||
use libc::getdtablesize;
|
||||
use wasmer_runtime_core::vm::Ctx;
|
||||
|
||||
pub fn setTempRet0(_ctx: &mut Ctx, _a: i32) {
|
||||
debug!("emscripten::setTempRet0");
|
||||
pub fn setTempRet0(ctx: &mut Ctx, val: i32) {
|
||||
debug!("emscripten::setTempRet0: {}", val);
|
||||
get_emscripten_data(ctx).temp_ret_0 = val;
|
||||
}
|
||||
pub fn getTempRet0(_ctx: &mut Ctx) -> i32 {
|
||||
|
||||
pub fn getTempRet0(ctx: &mut Ctx) -> i32 {
|
||||
debug!("emscripten::getTempRet0");
|
||||
get_emscripten_data(ctx).temp_ret_0
|
||||
}
|
||||
|
||||
pub fn _atexit(_ctx: &mut Ctx, _func: i32) -> i32 {
|
||||
debug!("emscripten::_atexit");
|
||||
// TODO: implement atexit properly
|
||||
// __ATEXIT__.unshift({
|
||||
// func: func,
|
||||
// arg: arg
|
||||
// });
|
||||
0
|
||||
}
|
||||
pub fn invoke_i(ctx: &mut Ctx, index: i32) -> i32 {
|
||||
debug!("emscripten::invoke_i");
|
||||
if let Some(dyn_call_i) = &get_emscripten_data(ctx).dyn_call_i {
|
||||
dyn_call_i.call(index).unwrap()
|
||||
} else {
|
||||
panic!("dyn_call_i is set to None");
|
||||
}
|
||||
}
|
||||
pub fn invoke_ii(ctx: &mut Ctx, index: i32, a1: i32) -> i32 {
|
||||
debug!("emscripten::invoke_ii");
|
||||
if let Some(dyn_call_ii) = &get_emscripten_data(ctx).dyn_call_ii {
|
||||
dyn_call_ii.call(index, a1).unwrap()
|
||||
} else {
|
||||
panic!("dyn_call_ii is set to None");
|
||||
}
|
||||
}
|
||||
pub fn invoke_iii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) -> i32 {
|
||||
debug!("emscripten::invoke_iii");
|
||||
if let Some(dyn_call_iii) = &get_emscripten_data(ctx).dyn_call_iii {
|
||||
dyn_call_iii.call(index, a1, a2).unwrap()
|
||||
} else {
|
||||
panic!("dyn_call_iii is set to None");
|
||||
}
|
||||
}
|
||||
pub fn invoke_iiii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) -> i32 {
|
||||
debug!("emscripten::invoke_iiii");
|
||||
if let Some(dyn_call_iiii) = &get_emscripten_data(ctx).dyn_call_iiii {
|
||||
dyn_call_iiii.call(index, a1, a2, a3).unwrap()
|
||||
} else {
|
||||
panic!("dyn_call_iiii is set to None");
|
||||
}
|
||||
}
|
||||
pub fn invoke_v(ctx: &mut Ctx, index: i32) {
|
||||
debug!("emscripten::invoke_v");
|
||||
if let Some(dyn_call_v) = &get_emscripten_data(ctx).dyn_call_v {
|
||||
dyn_call_v.call(index).unwrap();
|
||||
} else {
|
||||
panic!("dyn_call_v is set to None");
|
||||
}
|
||||
}
|
||||
pub fn invoke_vi(ctx: &mut Ctx, index: i32, a1: i32) {
|
||||
debug!("emscripten::invoke_vi");
|
||||
if let Some(dyn_call_vi) = &get_emscripten_data(ctx).dyn_call_vi {
|
||||
dyn_call_vi.call(index, a1).unwrap();
|
||||
} else {
|
||||
panic!("dyn_call_vi is set to None");
|
||||
}
|
||||
}
|
||||
pub fn invoke_vii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) {
|
||||
debug!("emscripten::invoke_vii");
|
||||
if let Some(dyn_call_vii) = &get_emscripten_data(ctx).dyn_call_vii {
|
||||
dyn_call_vii.call(index, a1, a2).unwrap();
|
||||
} else {
|
||||
panic!("dyn_call_vii is set to None");
|
||||
}
|
||||
}
|
||||
pub fn invoke_viii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) {
|
||||
debug!("emscripten::invoke_viii");
|
||||
if let Some(dyn_call_viii) = &get_emscripten_data(ctx).dyn_call_viii {
|
||||
dyn_call_viii.call(index, a1, a2, a3).unwrap();
|
||||
} else {
|
||||
panic!("dyn_call_viii is set to None");
|
||||
}
|
||||
}
|
||||
pub fn invoke_viiii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32) {
|
||||
debug!("emscripten::invoke_viiii");
|
||||
if let Some(dyn_call_viiii) = &get_emscripten_data(ctx).dyn_call_viiii {
|
||||
dyn_call_viiii.call(index, a1, a2, a3, a4).unwrap();
|
||||
} else {
|
||||
panic!("dyn_call_viiii is set to None");
|
||||
}
|
||||
}
|
||||
pub fn __Unwind_Backtrace(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 {
|
||||
debug!("emscripten::__Unwind_Backtrace");
|
||||
0
|
||||
@ -114,14 +54,65 @@ pub fn _dladdr(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 {
|
||||
debug!("emscripten::_dladdr");
|
||||
0
|
||||
}
|
||||
pub fn _pthread_attr_init(_ctx: &mut Ctx, _a: i32) -> i32 {
|
||||
debug!("emscripten::_pthread_attr_init({})", _a);
|
||||
0
|
||||
}
|
||||
pub fn _pthread_attr_destroy(_ctx: &mut Ctx, _a: i32) -> i32 {
|
||||
debug!("emscripten::_pthread_attr_destroy");
|
||||
0
|
||||
}
|
||||
pub fn _pthread_attr_getstack(
|
||||
_ctx: &mut Ctx,
|
||||
_stackaddr: i32,
|
||||
_stacksize: i32,
|
||||
_other: i32,
|
||||
) -> i32 {
|
||||
debug!(
|
||||
"emscripten::_pthread_attr_getstack({}, {}, {})",
|
||||
_stackaddr, _stacksize, _other
|
||||
);
|
||||
// TODO: Translate from Emscripten
|
||||
// HEAP32[stackaddr >> 2] = STACK_BASE;
|
||||
// HEAP32[stacksize >> 2] = TOTAL_STACK;
|
||||
0
|
||||
}
|
||||
pub fn _pthread_cond_destroy(_ctx: &mut Ctx, _a: i32) -> i32 {
|
||||
debug!("emscripten::_pthread_cond_destroy");
|
||||
0
|
||||
}
|
||||
pub fn _pthread_cond_timedwait(_ctx: &mut Ctx, _a: i32, _b: i32, _c: i32) -> i32 {
|
||||
debug!("emscripten::_pthread_cond_timedwait");
|
||||
0
|
||||
}
|
||||
pub fn _pthread_getspecific(_ctx: &mut Ctx, _a: i32) -> i32 {
|
||||
debug!("emscripten::_pthread_getspecific");
|
||||
0
|
||||
}
|
||||
pub fn _pthread_getattr_np(_ctx: &mut Ctx, _thread: i32, _attr: i32) -> i32 {
|
||||
debug!("emscripten::_pthread_getattr_np({}, {})", _thread, _attr);
|
||||
0
|
||||
}
|
||||
pub fn _pthread_setspecific(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 {
|
||||
debug!("emscripten::_pthread_setspecific");
|
||||
0
|
||||
}
|
||||
pub fn _pthread_once(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 {
|
||||
debug!("emscripten::_pthread_once");
|
||||
0
|
||||
}
|
||||
pub fn _pthread_key_create(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 {
|
||||
debug!("emscripten::_pthread_key_create");
|
||||
0
|
||||
}
|
||||
pub fn _pthread_create(_ctx: &mut Ctx, _a: i32, _b: i32, _c: i32, _d: i32) -> i32 {
|
||||
debug!("emscripten::_pthread_create");
|
||||
0
|
||||
}
|
||||
pub fn _pthread_detach(_ctx: &mut Ctx, _a: i32) -> i32 {
|
||||
debug!("emscripten::_pthread_detach");
|
||||
0
|
||||
}
|
||||
pub fn _pthread_join(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 {
|
||||
debug!("emscripten::_pthread_join");
|
||||
0
|
||||
@ -225,29 +216,96 @@ pub fn _getloadavg(_ctx: &mut Ctx, _loadavg: i32, _nelem: i32) -> i32 {
|
||||
debug!("emscripten::getloadavg");
|
||||
0
|
||||
}
|
||||
|
||||
// Invoke functions
|
||||
// They save the stack to allow unwinding
|
||||
|
||||
// Macro definitions
|
||||
macro_rules! invoke {
|
||||
($ctx: ident, $name:ident, $( $arg:ident ),*) => {{
|
||||
let sp = get_emscripten_data($ctx).stack_save.as_ref().expect("stack_save is None").call().expect("stack_save call failed");
|
||||
let result = get_emscripten_data($ctx).$name.as_ref().expect(concat!("Dynamic call is None: ", stringify!($name))).call($($arg),*);
|
||||
match result {
|
||||
Ok(v) => v,
|
||||
Err(_e) => {
|
||||
get_emscripten_data($ctx).stack_restore.as_ref().expect("stack_restore is None").call(sp).expect("stack_restore call failed");
|
||||
// TODO: We should check if _e != "longjmp" and if that's the case, re-throw the error
|
||||
// JS version is: if (e !== e+0 && e !== 'longjmp') throw e;
|
||||
get_emscripten_data($ctx).set_threw.as_ref().expect("set_threw is None").call(1, 0).expect("set_threw call failed");
|
||||
0 as _
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
macro_rules! invoke_no_return {
|
||||
($ctx: ident, $name:ident, $( $arg:ident ),*) => {{
|
||||
let sp = get_emscripten_data($ctx).stack_save.as_ref().expect("stack_save is None").call().expect("stack_save call failed");
|
||||
let result = get_emscripten_data($ctx).$name.as_ref().expect(concat!("Dynamic call is None: ", stringify!($name))).call($($arg),*);
|
||||
match result {
|
||||
Ok(v) => v,
|
||||
Err(_e) => {
|
||||
get_emscripten_data($ctx).stack_restore.as_ref().expect("stack_restore is None").call(sp).expect("stack_restore call failed");
|
||||
// TODO: We should check if _e != "longjmp" and if that's the case, re-throw the error
|
||||
// JS version is: if (e !== e+0 && e !== 'longjmp') throw e;
|
||||
get_emscripten_data($ctx).set_threw.as_ref().expect("set_threw is None").call(1, 0).expect("set_threw call failed");
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
// Invoke functions
|
||||
pub fn invoke_i(ctx: &mut Ctx, index: i32) -> i32 {
|
||||
debug!("emscripten::invoke_i");
|
||||
invoke!(ctx, dyn_call_i, index)
|
||||
}
|
||||
pub fn invoke_ii(ctx: &mut Ctx, index: i32, a1: i32) -> i32 {
|
||||
debug!("emscripten::invoke_ii");
|
||||
invoke!(ctx, dyn_call_ii, index, a1)
|
||||
}
|
||||
pub fn invoke_iii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) -> i32 {
|
||||
debug!("emscripten::invoke_iii");
|
||||
invoke!(ctx, dyn_call_iii, index, a1, a2)
|
||||
}
|
||||
pub fn invoke_iiii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) -> i32 {
|
||||
debug!("emscripten::invoke_iiii");
|
||||
invoke!(ctx, dyn_call_iiii, index, a1, a2, a3)
|
||||
}
|
||||
pub fn invoke_iifi(ctx: &mut Ctx, index: i32, a1: i32, a2: f64, a3: i32) -> i32 {
|
||||
debug!("emscripten::invoke_iifi");
|
||||
invoke!(ctx, dyn_call_iifi, index, a1, a2, a3)
|
||||
}
|
||||
pub fn invoke_v(ctx: &mut Ctx, index: i32) {
|
||||
debug!("emscripten::invoke_v");
|
||||
invoke_no_return!(ctx, dyn_call_v, index);
|
||||
}
|
||||
pub fn invoke_vi(ctx: &mut Ctx, index: i32, a1: i32) {
|
||||
debug!("emscripten::invoke_vi");
|
||||
invoke_no_return!(ctx, dyn_call_vi, index, a1);
|
||||
}
|
||||
pub fn invoke_vii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) {
|
||||
debug!("emscripten::invoke_vii");
|
||||
invoke_no_return!(ctx, dyn_call_vii, index, a1, a2);
|
||||
}
|
||||
|
||||
pub fn invoke_viii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) {
|
||||
debug!("emscripten::invoke_viii");
|
||||
invoke_no_return!(ctx, dyn_call_viii, index, a1, a2, a3);
|
||||
}
|
||||
pub fn invoke_viiii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32) {
|
||||
debug!("emscripten::invoke_viiii");
|
||||
invoke_no_return!(ctx, dyn_call_viiii, index, a1, a2, a3, a4);
|
||||
}
|
||||
pub fn invoke_dii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) -> f64 {
|
||||
debug!("emscripten::invoke_dii");
|
||||
if let Some(dyn_call_dii) = &get_emscripten_data(ctx).dyn_call_dii {
|
||||
dyn_call_dii.call(index, a1, a2).unwrap()
|
||||
} else {
|
||||
panic!("dyn_call_dii is set to None");
|
||||
}
|
||||
invoke!(ctx, dyn_call_dii, index, a1, a2)
|
||||
}
|
||||
pub fn invoke_diiii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32) -> f64 {
|
||||
debug!("emscripten::invoke_diiii");
|
||||
if let Some(dyn_call_diiii) = &get_emscripten_data(ctx).dyn_call_diiii {
|
||||
dyn_call_diiii.call(index, a1, a2, a3, a4).unwrap()
|
||||
} else {
|
||||
panic!("dyn_call_diiii is set to None");
|
||||
}
|
||||
invoke!(ctx, dyn_call_diiii, index, a1, a2, a3, a4)
|
||||
}
|
||||
pub fn invoke_iiiii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32) -> i32 {
|
||||
debug!("emscripten::invoke_iiiii");
|
||||
if let Some(dyn_call_iiiii) = &get_emscripten_data(ctx).dyn_call_iiiii {
|
||||
dyn_call_iiiii.call(index, a1, a2, a3, a4).unwrap()
|
||||
} else {
|
||||
panic!("dyn_call_iiiii is set to None");
|
||||
}
|
||||
invoke!(ctx, dyn_call_iiiii, index, a1, a2, a3, a4)
|
||||
}
|
||||
pub fn invoke_iiiiii(
|
||||
ctx: &mut Ctx,
|
||||
@ -259,11 +317,7 @@ pub fn invoke_iiiiii(
|
||||
a5: i32,
|
||||
) -> i32 {
|
||||
debug!("emscripten::invoke_iiiiii");
|
||||
if let Some(dyn_call_iiiiii) = &get_emscripten_data(ctx).dyn_call_iiiiii {
|
||||
dyn_call_iiiiii.call(index, a1, a2, a3, a4, a5).unwrap()
|
||||
} else {
|
||||
panic!("dyn_call_iiiiii is set to None");
|
||||
}
|
||||
invoke!(ctx, dyn_call_iiiiii, index, a1, a2, a3, a4, a5)
|
||||
}
|
||||
pub fn invoke_iiiiiii(
|
||||
ctx: &mut Ctx,
|
||||
@ -276,13 +330,7 @@ pub fn invoke_iiiiiii(
|
||||
a6: i32,
|
||||
) -> i32 {
|
||||
debug!("emscripten::invoke_iiiiiii");
|
||||
if let Some(dyn_call_iiiiiii) = &get_emscripten_data(ctx).dyn_call_iiiiiii {
|
||||
dyn_call_iiiiiii
|
||||
.call(index, a1, a2, a3, a4, a5, a6)
|
||||
.unwrap()
|
||||
} else {
|
||||
panic!("dyn_call_iiiiiii is set to None");
|
||||
}
|
||||
invoke!(ctx, dyn_call_iiiiiii, index, a1, a2, a3, a4, a5, a6)
|
||||
}
|
||||
pub fn invoke_iiiiiiii(
|
||||
ctx: &mut Ctx,
|
||||
@ -296,13 +344,34 @@ pub fn invoke_iiiiiiii(
|
||||
a7: i32,
|
||||
) -> i32 {
|
||||
debug!("emscripten::invoke_iiiiiiii");
|
||||
if let Some(dyn_call_iiiiiiii) = &get_emscripten_data(ctx).dyn_call_iiiiiiii {
|
||||
dyn_call_iiiiiiii
|
||||
.call(index, a1, a2, a3, a4, a5, a6, a7)
|
||||
.unwrap()
|
||||
} else {
|
||||
panic!("dyn_call_iiiiiiii is set to None");
|
||||
}
|
||||
invoke!(ctx, dyn_call_iiiiiiii, index, a1, a2, a3, a4, a5, a6, a7)
|
||||
}
|
||||
pub fn invoke_iiiiiiiii(
|
||||
ctx: &mut Ctx,
|
||||
index: i32,
|
||||
a1: i32,
|
||||
a2: i32,
|
||||
a3: i32,
|
||||
a4: i32,
|
||||
a5: i32,
|
||||
a6: i32,
|
||||
a7: i32,
|
||||
a8: i32,
|
||||
) -> i32 {
|
||||
debug!("emscripten::invoke_iiiiiiiii");
|
||||
invoke!(
|
||||
ctx,
|
||||
dyn_call_iiiiiiiii,
|
||||
index,
|
||||
a1,
|
||||
a2,
|
||||
a3,
|
||||
a4,
|
||||
a5,
|
||||
a6,
|
||||
a7,
|
||||
a8
|
||||
)
|
||||
}
|
||||
pub fn invoke_iiiiiiiiii(
|
||||
ctx: &mut Ctx,
|
||||
@ -318,29 +387,59 @@ pub fn invoke_iiiiiiiiii(
|
||||
a9: i32,
|
||||
) -> i32 {
|
||||
debug!("emscripten::invoke_iiiiiiiiii");
|
||||
if let Some(dyn_call_iiiiiiiiii) = &get_emscripten_data(ctx).dyn_call_iiiiiiiiii {
|
||||
dyn_call_iiiiiiiiii
|
||||
.call(index, a1, a2, a3, a4, a5, a6, a7, a8, a9)
|
||||
.unwrap()
|
||||
} else {
|
||||
panic!("dyn_call_iiiiiiiiii is set to None");
|
||||
}
|
||||
invoke!(
|
||||
ctx,
|
||||
dyn_call_iiiiiiiiii,
|
||||
index,
|
||||
a1,
|
||||
a2,
|
||||
a3,
|
||||
a4,
|
||||
a5,
|
||||
a6,
|
||||
a7,
|
||||
a8,
|
||||
a9
|
||||
)
|
||||
}
|
||||
pub fn invoke_iiiiiiiiiii(
|
||||
ctx: &mut Ctx,
|
||||
index: i32,
|
||||
a1: i32,
|
||||
a2: i32,
|
||||
a3: i32,
|
||||
a4: i32,
|
||||
a5: i32,
|
||||
a6: i32,
|
||||
a7: i32,
|
||||
a8: i32,
|
||||
a9: i32,
|
||||
a10: i32,
|
||||
) -> i32 {
|
||||
debug!("emscripten::invoke_iiiiiiiiiii");
|
||||
invoke!(
|
||||
ctx,
|
||||
dyn_call_iiiiiiiiiii,
|
||||
index,
|
||||
a1,
|
||||
a2,
|
||||
a3,
|
||||
a4,
|
||||
a5,
|
||||
a6,
|
||||
a7,
|
||||
a8,
|
||||
a9,
|
||||
a10
|
||||
)
|
||||
}
|
||||
pub fn invoke_vd(ctx: &mut Ctx, index: i32, a1: f64) {
|
||||
debug!("emscripten::invoke_vd");
|
||||
if let Some(dyn_call_vd) = &get_emscripten_data(ctx).dyn_call_vd {
|
||||
dyn_call_vd.call(index, a1).unwrap();
|
||||
} else {
|
||||
panic!("dyn_call_vd is set to None");
|
||||
}
|
||||
invoke_no_return!(ctx, dyn_call_vd, index, a1)
|
||||
}
|
||||
pub fn invoke_viiiii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32, a5: i32) {
|
||||
debug!("emscripten::invoke_viiiii");
|
||||
if let Some(dyn_call_viiiii) = &get_emscripten_data(ctx).dyn_call_viiiii {
|
||||
dyn_call_viiiii.call(index, a1, a2, a3, a4, a5).unwrap();
|
||||
} else {
|
||||
panic!("dyn_call_viiiii is set to None");
|
||||
}
|
||||
invoke_no_return!(ctx, dyn_call_viiiii, index, a1, a2, a3, a4, a5)
|
||||
}
|
||||
pub fn invoke_viiiiii(
|
||||
ctx: &mut Ctx,
|
||||
@ -353,13 +452,7 @@ pub fn invoke_viiiiii(
|
||||
a6: i32,
|
||||
) {
|
||||
debug!("emscripten::invoke_viiiiii");
|
||||
if let Some(dyn_call_viiiiii) = &get_emscripten_data(ctx).dyn_call_viiiiii {
|
||||
dyn_call_viiiiii
|
||||
.call(index, a1, a2, a3, a4, a5, a6)
|
||||
.unwrap();
|
||||
} else {
|
||||
panic!("dyn_call_viiiiii is set to None");
|
||||
}
|
||||
invoke_no_return!(ctx, dyn_call_viiiiii, index, a1, a2, a3, a4, a5, a6)
|
||||
}
|
||||
pub fn invoke_viiiiiii(
|
||||
ctx: &mut Ctx,
|
||||
@ -373,13 +466,7 @@ pub fn invoke_viiiiiii(
|
||||
a7: i32,
|
||||
) {
|
||||
debug!("emscripten::invoke_viiiiiii");
|
||||
if let Some(dyn_call_viiiiiii) = &get_emscripten_data(ctx).dyn_call_viiiiiii {
|
||||
dyn_call_viiiiiii
|
||||
.call(index, a1, a2, a3, a4, a5, a6, a7)
|
||||
.unwrap();
|
||||
} else {
|
||||
panic!("dyn_call_viiiiiii is set to None");
|
||||
}
|
||||
invoke_no_return!(ctx, dyn_call_viiiiiii, index, a1, a2, a3, a4, a5, a6, a7)
|
||||
}
|
||||
pub fn invoke_viiiiiiii(
|
||||
ctx: &mut Ctx,
|
||||
@ -394,13 +481,19 @@ pub fn invoke_viiiiiiii(
|
||||
a8: i32,
|
||||
) {
|
||||
debug!("emscripten::invoke_viiiiiiii");
|
||||
if let Some(dyn_call_viiiiiiii) = &get_emscripten_data(ctx).dyn_call_viiiiiiii {
|
||||
dyn_call_viiiiiiii
|
||||
.call(index, a1, a2, a3, a4, a5, a6, a7, a8)
|
||||
.unwrap();
|
||||
} else {
|
||||
panic!("dyn_call_viiiiiiii is set to None");
|
||||
}
|
||||
invoke_no_return!(
|
||||
ctx,
|
||||
dyn_call_viiiiiiii,
|
||||
index,
|
||||
a1,
|
||||
a2,
|
||||
a3,
|
||||
a4,
|
||||
a5,
|
||||
a6,
|
||||
a7,
|
||||
a8
|
||||
)
|
||||
}
|
||||
pub fn invoke_viiiiiiiii(
|
||||
ctx: &mut Ctx,
|
||||
@ -416,21 +509,75 @@ pub fn invoke_viiiiiiiii(
|
||||
a9: i32,
|
||||
) {
|
||||
debug!("emscripten::invoke_viiiiiiiii");
|
||||
if let Some(dyn_call_viiiiiiiii) = &get_emscripten_data(ctx).dyn_call_viiiiiiiii {
|
||||
dyn_call_viiiiiiiii
|
||||
.call(index, a1, a2, a3, a4, a5, a6, a7, a8, a9)
|
||||
.unwrap();
|
||||
} else {
|
||||
panic!("dyn_call_viiiiiiiii is set to None");
|
||||
}
|
||||
invoke_no_return!(
|
||||
ctx,
|
||||
dyn_call_viiiiiiiii,
|
||||
index,
|
||||
a1,
|
||||
a2,
|
||||
a3,
|
||||
a4,
|
||||
a5,
|
||||
a6,
|
||||
a7,
|
||||
a8,
|
||||
a9
|
||||
)
|
||||
}
|
||||
pub fn invoke_viiiiiiiiii(
|
||||
ctx: &mut Ctx,
|
||||
index: i32,
|
||||
a1: i32,
|
||||
a2: i32,
|
||||
a3: i32,
|
||||
a4: i32,
|
||||
a5: i32,
|
||||
a6: i32,
|
||||
a7: i32,
|
||||
a8: i32,
|
||||
a9: i32,
|
||||
a10: i32,
|
||||
) {
|
||||
debug!("emscripten::invoke_viiiiiiiiii");
|
||||
invoke_no_return!(
|
||||
ctx,
|
||||
dyn_call_viiiiiiiiii,
|
||||
index,
|
||||
a1,
|
||||
a2,
|
||||
a3,
|
||||
a4,
|
||||
a5,
|
||||
a6,
|
||||
a7,
|
||||
a8,
|
||||
a9,
|
||||
a10
|
||||
)
|
||||
}
|
||||
|
||||
pub fn invoke_iij(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) -> i32 {
|
||||
debug!("emscripten::invoke_iij");
|
||||
invoke!(ctx, dyn_call_iij, index, a1, a2, a3)
|
||||
}
|
||||
|
||||
pub fn invoke_iiji(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32) -> i32 {
|
||||
debug!("emscripten::invoke_iiji");
|
||||
if let Some(dyn_call_iiji) = &get_emscripten_data(ctx).dyn_call_iiji {
|
||||
dyn_call_iiji.call(index, a1, a2, a3, a4).unwrap()
|
||||
} else {
|
||||
panic!("dyn_call_iiji is set to None");
|
||||
}
|
||||
invoke!(ctx, dyn_call_iiji, index, a1, a2, a3, a4)
|
||||
}
|
||||
|
||||
pub fn invoke_iiijj(
|
||||
ctx: &mut Ctx,
|
||||
index: i32,
|
||||
a1: i32,
|
||||
a2: i32,
|
||||
a3: i32,
|
||||
a4: i32,
|
||||
a5: i32,
|
||||
a6: i32,
|
||||
) -> i32 {
|
||||
debug!("emscripten::invoke_iiijj");
|
||||
invoke!(ctx, dyn_call_iiijj, index, a1, a2, a3, a4, a5, a6)
|
||||
}
|
||||
pub fn invoke_j(ctx: &mut Ctx, index: i32) -> i32 {
|
||||
debug!("emscripten::invoke_j");
|
||||
@ -448,6 +595,15 @@ pub fn invoke_ji(ctx: &mut Ctx, index: i32, a1: i32) -> i32 {
|
||||
panic!("dyn_call_ji is set to None");
|
||||
}
|
||||
}
|
||||
pub fn invoke_jii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) -> i32 {
|
||||
debug!("emscripten::invoke_jii");
|
||||
if let Some(dyn_call_jii) = &get_emscripten_data(ctx).dyn_call_jii {
|
||||
dyn_call_jii.call(index, a1, a2).unwrap()
|
||||
} else {
|
||||
panic!("dyn_call_jii is set to None");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invoke_jij(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) -> i32 {
|
||||
debug!("emscripten::invoke_jij");
|
||||
if let Some(dyn_call_jij) = &get_emscripten_data(ctx).dyn_call_jij {
|
||||
@ -571,6 +727,18 @@ pub fn invoke_viijj(
|
||||
panic!("dyn_call_viijj is set to None");
|
||||
}
|
||||
}
|
||||
pub fn invoke_vj(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) {
|
||||
debug!("emscripten::invoke_vj");
|
||||
if let Some(dyn_call_vj) = &get_emscripten_data(ctx).dyn_call_vj {
|
||||
dyn_call_vj.call(index, a1, a2).unwrap();
|
||||
} else {
|
||||
panic!("dyn_call_vj is set to None");
|
||||
}
|
||||
}
|
||||
pub fn invoke_vjji(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32, a5: i32) {
|
||||
debug!("emscripten::invoke_vjji");
|
||||
invoke_no_return!(ctx, dyn_call_vjji, index, a1, a2, a3, a4, a5)
|
||||
}
|
||||
pub fn invoke_vij(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) {
|
||||
debug!("emscripten::invoke_vij");
|
||||
if let Some(dyn_call_vij) = &get_emscripten_data(ctx).dyn_call_vij {
|
||||
@ -612,11 +780,42 @@ pub fn invoke_vijj(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32
|
||||
panic!("dyn_call_vijj is set to None");
|
||||
}
|
||||
}
|
||||
pub fn invoke_viid(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: f64) {
|
||||
debug!("emscripten::invoke_viid");
|
||||
invoke_no_return!(ctx, dyn_call_viid, index, a1, a2, a3);
|
||||
}
|
||||
pub fn invoke_viidii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: f64, a4: i32, a5: i32) {
|
||||
debug!("emscripten::invoke_viidii");
|
||||
if let Some(dyn_call_viidii) = &get_emscripten_data(ctx).dyn_call_viidii {
|
||||
dyn_call_viidii.call(index, a1, a2, a3, a4, a5).unwrap();
|
||||
} else {
|
||||
panic!("dyn_call_viidii is set to None");
|
||||
}
|
||||
invoke_no_return!(ctx, dyn_call_viidii, index, a1, a2, a3, a4, a5);
|
||||
}
|
||||
pub fn invoke_viidddddddd(
|
||||
ctx: &mut Ctx,
|
||||
index: i32,
|
||||
a1: i32,
|
||||
a2: i32,
|
||||
a3: f64,
|
||||
a4: f64,
|
||||
a5: f64,
|
||||
a6: f64,
|
||||
a7: f64,
|
||||
a8: f64,
|
||||
a9: f64,
|
||||
a10: f64,
|
||||
) {
|
||||
debug!("emscripten::invoke_viidddddddd");
|
||||
invoke_no_return!(
|
||||
ctx,
|
||||
dyn_call_viidddddddd,
|
||||
index,
|
||||
a1,
|
||||
a2,
|
||||
a3,
|
||||
a4,
|
||||
a5,
|
||||
a6,
|
||||
a7,
|
||||
a8,
|
||||
a9,
|
||||
a10
|
||||
);
|
||||
}
|
||||
|
47
lib/emscripten/src/env/mod.rs
vendored
47
lib/emscripten/src/env/mod.rs
vendored
@ -47,27 +47,64 @@ pub fn _getpagesize(_ctx: &mut Ctx) -> u32 {
|
||||
16384
|
||||
}
|
||||
|
||||
pub fn _times(ctx: &mut Ctx, buffer: u32) -> u32 {
|
||||
if buffer != 0 {
|
||||
call_memset(ctx, buffer, 0, 16);
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
pub fn ___build_environment(ctx: &mut Ctx, environ: c_int) {
|
||||
debug!("emscripten::___build_environment {}", environ);
|
||||
const MAX_ENV_VALUES: u32 = 64;
|
||||
const TOTAL_ENV_SIZE: u32 = 1024;
|
||||
let environment = emscripten_memory_pointer!(ctx.memory(0), environ) as *mut c_int;
|
||||
unsafe {
|
||||
let (mut pool_offset, env_ptr, mut pool_ptr) = unsafe {
|
||||
let (pool_offset, _pool_slice): (u32, &mut [u8]) =
|
||||
allocate_on_stack(ctx, TOTAL_ENV_SIZE as u32);
|
||||
let (env_offset, _env_slice): (u32, &mut [u8]) =
|
||||
allocate_on_stack(ctx, (MAX_ENV_VALUES * 4) as u32);
|
||||
let env_ptr = emscripten_memory_pointer!(ctx.memory(0), env_offset) as *mut c_int;
|
||||
let mut _pool_ptr = emscripten_memory_pointer!(ctx.memory(0), pool_offset) as *mut c_int;
|
||||
let pool_ptr = emscripten_memory_pointer!(ctx.memory(0), pool_offset) as *mut u8;
|
||||
*env_ptr = pool_offset as i32;
|
||||
*environment = env_offset as i32;
|
||||
|
||||
// *env_ptr = 0;
|
||||
(pool_offset, env_ptr, pool_ptr)
|
||||
};
|
||||
// unsafe {
|
||||
|
||||
// *env_ptr = 0;
|
||||
// };
|
||||
let default_vars = vec![
|
||||
["USER", "web_user"],
|
||||
["LOGNAME", "web_user"],
|
||||
["PATH", "/"],
|
||||
["PWD", "/"],
|
||||
["HOME", "/home/web_user"],
|
||||
["LANG", "C.UTF-8"],
|
||||
["_", "thisProgram"],
|
||||
];
|
||||
let mut strings = vec![];
|
||||
let mut total_size = 0;
|
||||
for [key, val] in &default_vars {
|
||||
let line = key.to_string() + "=" + val;
|
||||
total_size += line.len();
|
||||
strings.push(line);
|
||||
}
|
||||
if total_size as u32 > TOTAL_ENV_SIZE {
|
||||
panic!("Environment size exceeded TOTAL_ENV_SIZE!");
|
||||
}
|
||||
unsafe {
|
||||
for (i, s) in strings.iter().enumerate() {
|
||||
for (j, c) in s.chars().enumerate() {
|
||||
debug_assert!(c < u8::max_value() as char);
|
||||
*pool_ptr.add(j) = c as u8;
|
||||
}
|
||||
*env_ptr.add(i * 4) = pool_offset as i32;
|
||||
pool_offset += s.len() as u32 + 1;
|
||||
pool_ptr = pool_ptr.add(s.len() + 1);
|
||||
}
|
||||
*env_ptr.add(strings.len() * 4) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ___assert_fail(_ctx: &mut Ctx, _a: c_int, _b: c_int, _c: c_int, _d: c_int) {
|
||||
|
@ -14,3 +14,23 @@ pub fn ___cxa_throw(ctx: &mut Ctx, _ptr: u32, _ty: u32, _destructor: u32) {
|
||||
debug!("emscripten::___cxa_throw");
|
||||
_abort(ctx);
|
||||
}
|
||||
|
||||
pub fn ___cxa_begin_catch(_ctx: &mut Ctx, _exception_object_ptr: u32) -> i32 {
|
||||
debug!("emscripten::___cxa_begin_catch");
|
||||
-1
|
||||
}
|
||||
|
||||
pub fn ___cxa_end_catch(_ctx: &mut Ctx) {
|
||||
debug!("emscripten::___cxa_end_catch");
|
||||
}
|
||||
|
||||
pub fn ___cxa_uncaught_exception(_ctx: &mut Ctx) -> i32 {
|
||||
debug!("emscripten::___cxa_uncaught_exception");
|
||||
-1
|
||||
}
|
||||
|
||||
pub fn ___cxa_pure_virtual(_ctx: &mut Ctx) {
|
||||
debug!("emscripten::___cxa_pure_virtual");
|
||||
// ABORT = true
|
||||
panic!("Pure virtual function called!");
|
||||
}
|
||||
|
@ -25,21 +25,35 @@ pub fn getprotobynumber(_ctx: &mut Ctx, _one: i32) -> i32 {
|
||||
}
|
||||
|
||||
/// sigdelset
|
||||
pub fn sigdelset(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
|
||||
pub fn sigdelset(ctx: &mut Ctx, set: i32, signum: i32) -> i32 {
|
||||
debug!("emscripten::sigdelset");
|
||||
unimplemented!()
|
||||
let memory = ctx.memory(0);
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let ptr = emscripten_memory_pointer!(memory, set) as *mut i32;
|
||||
|
||||
unsafe { *ptr = *ptr & !(1 << (signum - 1)) }
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
/// sigfillset
|
||||
pub fn sigfillset(_ctx: &mut Ctx, _one: i32) -> i32 {
|
||||
pub fn sigfillset(ctx: &mut Ctx, set: i32) -> i32 {
|
||||
debug!("emscripten::sigfillset");
|
||||
unimplemented!()
|
||||
let memory = ctx.memory(0);
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let ptr = emscripten_memory_pointer!(memory, set) as *mut i32;
|
||||
|
||||
unsafe {
|
||||
*ptr = -1;
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
/// tzset
|
||||
pub fn tzset(_ctx: &mut Ctx) {
|
||||
debug!("emscripten::tzset");
|
||||
unimplemented!()
|
||||
debug!("emscripten::tzset - stub");
|
||||
//unimplemented!()
|
||||
}
|
||||
|
||||
/// strptime
|
||||
|
@ -1,43 +1,61 @@
|
||||
use super::env::get_emscripten_data;
|
||||
use libc::{c_int, c_void};
|
||||
use std::cell::UnsafeCell;
|
||||
use super::process::abort_with_message;
|
||||
use libc::c_int;
|
||||
// use std::cell::UnsafeCell;
|
||||
use wasmer_runtime_core::vm::Ctx;
|
||||
|
||||
/// setjmp
|
||||
pub fn __setjmp(ctx: &mut Ctx, env_addr: u32) -> c_int {
|
||||
pub fn __setjmp(ctx: &mut Ctx, _env_addr: u32) -> c_int {
|
||||
debug!("emscripten::__setjmp (setjmp)");
|
||||
unsafe {
|
||||
// Rather than using the env as the holder of the jump buffer pointer,
|
||||
// we use the environment address to store the index relative to jumps
|
||||
// so the address of the jump it's outside the wasm memory itself.
|
||||
let jump_index = emscripten_memory_pointer!(ctx.memory(0), env_addr) as *mut i8;
|
||||
// We create the jump buffer outside of the wasm memory
|
||||
let jump_buf: UnsafeCell<[u32; 27]> = UnsafeCell::new([0; 27]);
|
||||
let jumps = &mut get_emscripten_data(ctx).jumps;
|
||||
let result = setjmp(jump_buf.get() as _);
|
||||
// We set the jump index to be the last 3value of jumps
|
||||
*jump_index = jumps.len() as _;
|
||||
// We hold the reference of the jump buffer
|
||||
jumps.push(jump_buf);
|
||||
result
|
||||
}
|
||||
abort_with_message(ctx, "missing function: _setjmp");
|
||||
unreachable!()
|
||||
// unsafe {
|
||||
// // Rather than using the env as the holder of the jump buffer pointer,
|
||||
// // we use the environment address to store the index relative to jumps
|
||||
// // so the address of the jump it's outside the wasm memory itself.
|
||||
// let jump_index = emscripten_memory_pointer!(ctx.memory(0), env_addr) as *mut i8;
|
||||
// // We create the jump buffer outside of the wasm memory
|
||||
// let jump_buf: UnsafeCell<[u32; 27]> = UnsafeCell::new([0; 27]);
|
||||
// let jumps = &mut get_emscripten_data(ctx).jumps;
|
||||
// let result = setjmp(jump_buf.get() as _);
|
||||
// // We set the jump index to be the last 3value of jumps
|
||||
// *jump_index = jumps.len() as _;
|
||||
// // We hold the reference of the jump buffer
|
||||
// jumps.push(jump_buf);
|
||||
// result
|
||||
// }
|
||||
}
|
||||
|
||||
/// longjmp
|
||||
#[allow(unreachable_code)]
|
||||
pub fn __longjmp(ctx: &mut Ctx, env_addr: u32, val: c_int) {
|
||||
pub fn __longjmp(ctx: &mut Ctx, _env_addr: u32, _val: c_int) {
|
||||
debug!("emscripten::__longjmp (longmp)");
|
||||
unsafe {
|
||||
// We retrieve the jump index from the env address
|
||||
let jump_index = emscripten_memory_pointer!(ctx.memory(0), env_addr) as *mut i8;
|
||||
let jumps = &mut get_emscripten_data(ctx).jumps;
|
||||
// We get the real jump buffer from the jumps vector, using the retrieved index
|
||||
let jump_buf = &jumps[*jump_index as usize];
|
||||
longjmp(jump_buf.get() as _, val)
|
||||
};
|
||||
abort_with_message(ctx, "missing function: _longjmp");
|
||||
// unsafe {
|
||||
// // We retrieve the jump index from the env address
|
||||
// let jump_index = emscripten_memory_pointer!(ctx.memory(0), env_addr) as *mut i8;
|
||||
// let jumps = &mut get_emscripten_data(ctx).jumps;
|
||||
// // We get the real jump buffer from the jumps vector, using the retrieved index
|
||||
// let jump_buf = &jumps[*jump_index as usize];
|
||||
// longjmp(jump_buf.get() as _, val)
|
||||
// };
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn setjmp(env: *mut c_void) -> c_int;
|
||||
fn longjmp(env: *mut c_void, val: c_int) -> !;
|
||||
/// _longjmp
|
||||
// This function differs from the js implementation, it should return Result<(), &'static str>
|
||||
pub fn _longjmp(ctx: &mut Ctx, env_addr: i32, val: c_int) -> Result<(), ()> {
|
||||
let val = if val == 0 { 1 } else { val };
|
||||
get_emscripten_data(ctx)
|
||||
.set_threw
|
||||
.as_ref()
|
||||
.expect("set_threw is None")
|
||||
.call(env_addr, val)
|
||||
.expect("set_threw failed to call");
|
||||
// TODO: return Err("longjmp")
|
||||
Err(())
|
||||
}
|
||||
|
||||
// extern "C" {
|
||||
// fn setjmp(env: *mut c_void) -> c_int;
|
||||
// fn longjmp(env: *mut c_void, val: c_int) -> !;
|
||||
// }
|
||||
|
@ -1,3 +1,5 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate wasmer_runtime_core;
|
||||
|
||||
@ -50,7 +52,7 @@ mod varargs;
|
||||
|
||||
pub use self::storage::{align_memory, static_alloc};
|
||||
pub use self::utils::{
|
||||
allocate_cstr_on_stack, allocate_on_stack, get_emscripten_memory_size,
|
||||
allocate_cstr_on_stack, allocate_on_stack, get_emscripten_memory_size, get_emscripten_metadata,
|
||||
get_emscripten_table_size, is_emscripten_module,
|
||||
};
|
||||
|
||||
@ -83,6 +85,7 @@ pub struct EmscriptenData<'a> {
|
||||
pub dyn_call_ii: Option<Func<'a, (i32, i32), i32>>,
|
||||
pub dyn_call_iii: Option<Func<'a, (i32, i32, i32), i32>>,
|
||||
pub dyn_call_iiii: Option<Func<'a, (i32, i32, i32, i32), i32>>,
|
||||
pub dyn_call_iifi: Option<Func<'a, (i32, i32, f64, i32), i32>>,
|
||||
pub dyn_call_v: Option<Func<'a, (i32)>>,
|
||||
pub dyn_call_vi: Option<Func<'a, (i32, i32)>>,
|
||||
pub dyn_call_vii: Option<Func<'a, (i32, i32, i32)>>,
|
||||
@ -96,17 +99,25 @@ pub struct EmscriptenData<'a> {
|
||||
pub dyn_call_iiiiii: Option<Func<'a, (i32, i32, i32, i32, i32, i32), i32>>,
|
||||
pub dyn_call_iiiiiii: Option<Func<'a, (i32, i32, i32, i32, i32, i32, i32), i32>>,
|
||||
pub dyn_call_iiiiiiii: Option<Func<'a, (i32, i32, i32, i32, i32, i32, i32, i32), i32>>,
|
||||
pub dyn_call_iiiiiiiii: Option<Func<'a, (i32, i32, i32, i32, i32, i32, i32, i32, i32), i32>>,
|
||||
pub dyn_call_iiiiiiiiii:
|
||||
Option<Func<'a, (i32, i32, i32, i32, i32, i32, i32, i32, i32, i32), i32>>,
|
||||
pub dyn_call_iiiiiiiiiii:
|
||||
Option<Func<'a, (i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32), i32>>,
|
||||
pub dyn_call_vd: Option<Func<'a, (i32, f64)>>,
|
||||
pub dyn_call_viiiii: Option<Func<'a, (i32, i32, i32, i32, i32, i32)>>,
|
||||
pub dyn_call_viiiiii: Option<Func<'a, (i32, i32, i32, i32, i32, i32, i32)>>,
|
||||
pub dyn_call_viiiiiii: Option<Func<'a, (i32, i32, i32, i32, i32, i32, i32, i32)>>,
|
||||
pub dyn_call_viiiiiiii: Option<Func<'a, (i32, i32, i32, i32, i32, i32, i32, i32, i32)>>,
|
||||
pub dyn_call_viiiiiiiii: Option<Func<'a, (i32, i32, i32, i32, i32, i32, i32, i32, i32, i32)>>,
|
||||
pub dyn_call_viiiiiiiiii:
|
||||
Option<Func<'a, (i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32)>>,
|
||||
pub dyn_call_iij: Option<Func<'a, (i32, i32, i32, i32), i32>>,
|
||||
pub dyn_call_iiji: Option<Func<'a, (i32, i32, i32, i32, i32), i32>>,
|
||||
pub dyn_call_iiijj: Option<Func<'a, (i32, i32, i32, i32, i32, i32, i32), i32>>,
|
||||
pub dyn_call_j: Option<Func<'a, i32, i32>>,
|
||||
pub dyn_call_ji: Option<Func<'a, (i32, i32), i32>>,
|
||||
pub dyn_call_jii: Option<Func<'a, (i32, i32, i32), i32>>,
|
||||
pub dyn_call_jij: Option<Func<'a, (i32, i32, i32, i32), i32>>,
|
||||
pub dyn_call_jjj: Option<Func<'a, (i32, i32, i32, i32, i32), i32>>,
|
||||
pub dyn_call_viiij: Option<Func<'a, (i32, i32, i32, i32, i32, i32)>>,
|
||||
@ -117,22 +128,28 @@ pub struct EmscriptenData<'a> {
|
||||
pub dyn_call_viiji: Option<Func<'a, (i32, i32, i32, i32, i32, i32)>>,
|
||||
pub dyn_call_viijiii: Option<Func<'a, (i32, i32, i32, i32, i32, i32, i32, i32)>>,
|
||||
pub dyn_call_viijj: Option<Func<'a, (i32, i32, i32, i32, i32, i32, i32)>>,
|
||||
pub dyn_call_vj: Option<Func<'a, (i32, i32, i32)>>,
|
||||
pub dyn_call_vjji: Option<Func<'a, (i32, i32, i32, i32, i32, i32)>>,
|
||||
pub dyn_call_vij: Option<Func<'a, (i32, i32, i32, i32)>>,
|
||||
pub dyn_call_viji: Option<Func<'a, (i32, i32, i32, i32, i32)>>,
|
||||
pub dyn_call_vijiii: Option<Func<'a, (i32, i32, i32, i32, i32, i32, i32)>>,
|
||||
pub dyn_call_vijj: Option<Func<'a, (i32, i32, i32, i32, i32, i32)>>,
|
||||
pub dyn_call_viid: Option<Func<'a, (i32, i32, i32, f64)>>,
|
||||
pub dyn_call_viidii: Option<Func<'a, (i32, i32, i32, f64, i32, i32)>>,
|
||||
pub dyn_call_viidddddddd:
|
||||
Option<Func<'a, (i32, i32, i32, f64, f64, f64, f64, f64, f64, f64, f64)>>,
|
||||
pub temp_ret_0: i32,
|
||||
|
||||
pub stack_save: Option<Func<'a, (), i32>>,
|
||||
pub stack_restore: Option<Func<'a, (i32)>>,
|
||||
pub set_threw: Option<Func<'a, (i32, i32)>>,
|
||||
}
|
||||
|
||||
impl<'a> EmscriptenData<'a> {
|
||||
pub fn new(instance: &'a mut Instance) -> EmscriptenData<'a> {
|
||||
let malloc = instance.func("_malloc").unwrap();
|
||||
let free = instance.func("_free").unwrap();
|
||||
let memalign = if let Ok(func) = instance.func("_memalign") {
|
||||
Some(func)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let memalign = instance.func("_memalign").ok();
|
||||
let memset = instance.func("_memset").unwrap();
|
||||
let stack_alloc = instance.func("stackAlloc").unwrap();
|
||||
|
||||
@ -140,6 +157,7 @@ impl<'a> EmscriptenData<'a> {
|
||||
let dyn_call_ii = instance.func("dynCall_ii").ok();
|
||||
let dyn_call_iii = instance.func("dynCall_iii").ok();
|
||||
let dyn_call_iiii = instance.func("dynCall_iiii").ok();
|
||||
let dyn_call_iifi = instance.func("dynCall_iifi").ok();
|
||||
let dyn_call_v = instance.func("dynCall_v").ok();
|
||||
let dyn_call_vi = instance.func("dynCall_vi").ok();
|
||||
let dyn_call_vii = instance.func("dynCall_vii").ok();
|
||||
@ -153,16 +171,22 @@ impl<'a> EmscriptenData<'a> {
|
||||
let dyn_call_iiiiii = instance.func("dynCall_iiiiii").ok();
|
||||
let dyn_call_iiiiiii = instance.func("dynCall_iiiiiii").ok();
|
||||
let dyn_call_iiiiiiii = instance.func("dynCall_iiiiiiii").ok();
|
||||
let dyn_call_iiiiiiiii = instance.func("dynCall_iiiiiiiii").ok();
|
||||
let dyn_call_iiiiiiiiii = instance.func("dynCall_iiiiiiiiii").ok();
|
||||
let dyn_call_iiiiiiiiiii = instance.func("dynCall_iiiiiiiiiii").ok();
|
||||
let dyn_call_vd = instance.func("dynCall_vd").ok();
|
||||
let dyn_call_viiiii = instance.func("dynCall_viiiii").ok();
|
||||
let dyn_call_viiiiii = instance.func("dynCall_viiiiii").ok();
|
||||
let dyn_call_viiiiiii = instance.func("dynCall_viiiiiii").ok();
|
||||
let dyn_call_viiiiiiii = instance.func("dynCall_viiiiiiii").ok();
|
||||
let dyn_call_viiiiiiiii = instance.func("dynCall_viiiiiiiii").ok();
|
||||
let dyn_call_viiiiiiiiii = instance.func("dynCall_viiiiiiiiii").ok();
|
||||
let dyn_call_iij = instance.func("dynCall_iij").ok();
|
||||
let dyn_call_iiji = instance.func("dynCall_iiji").ok();
|
||||
let dyn_call_iiijj = instance.func("dynCall_iiijj").ok();
|
||||
let dyn_call_j = instance.func("dynCall_j").ok();
|
||||
let dyn_call_ji = instance.func("dynCall_ji").ok();
|
||||
let dyn_call_jii = instance.func("dynCall_jii").ok();
|
||||
let dyn_call_jij = instance.func("dynCall_jij").ok();
|
||||
let dyn_call_jjj = instance.func("dynCall_jjj").ok();
|
||||
let dyn_call_viiij = instance.func("dynCall_viiij").ok();
|
||||
@ -172,11 +196,19 @@ impl<'a> EmscriptenData<'a> {
|
||||
let dyn_call_viiji = instance.func("dynCall_viiji").ok();
|
||||
let dyn_call_viijiii = instance.func("dynCall_viijiii").ok();
|
||||
let dyn_call_viijj = instance.func("dynCall_viijj").ok();
|
||||
let dyn_call_vj = instance.func("dynCall_vj").ok();
|
||||
let dyn_call_vjji = instance.func("dynCall_vjji").ok();
|
||||
let dyn_call_vij = instance.func("dynCall_vij").ok();
|
||||
let dyn_call_viji = instance.func("dynCall_viji").ok();
|
||||
let dyn_call_vijiii = instance.func("dynCall_vijiii").ok();
|
||||
let dyn_call_vijj = instance.func("dynCall_vijj").ok();
|
||||
let dyn_call_viid = instance.func("dynCall_viid").ok();
|
||||
let dyn_call_viidii = instance.func("dynCall_viidii").ok();
|
||||
let dyn_call_viidddddddd = instance.func("dynCall_viidddddddd").ok();
|
||||
|
||||
let stack_save = instance.func("stackSave").ok();
|
||||
let stack_restore = instance.func("stackRestore").ok();
|
||||
let set_threw = instance.func("_setThrew").ok();
|
||||
|
||||
EmscriptenData {
|
||||
malloc,
|
||||
@ -189,6 +221,7 @@ impl<'a> EmscriptenData<'a> {
|
||||
dyn_call_ii,
|
||||
dyn_call_iii,
|
||||
dyn_call_iiii,
|
||||
dyn_call_iifi,
|
||||
dyn_call_v,
|
||||
dyn_call_vi,
|
||||
dyn_call_vii,
|
||||
@ -202,16 +235,22 @@ impl<'a> EmscriptenData<'a> {
|
||||
dyn_call_iiiiii,
|
||||
dyn_call_iiiiiii,
|
||||
dyn_call_iiiiiiii,
|
||||
dyn_call_iiiiiiiii,
|
||||
dyn_call_iiiiiiiiii,
|
||||
dyn_call_iiiiiiiiiii,
|
||||
dyn_call_vd,
|
||||
dyn_call_viiiii,
|
||||
dyn_call_viiiiii,
|
||||
dyn_call_viiiiiii,
|
||||
dyn_call_viiiiiiii,
|
||||
dyn_call_viiiiiiiii,
|
||||
dyn_call_viiiiiiiiii,
|
||||
dyn_call_iij,
|
||||
dyn_call_iiji,
|
||||
dyn_call_iiijj,
|
||||
dyn_call_j,
|
||||
dyn_call_ji,
|
||||
dyn_call_jii,
|
||||
dyn_call_jij,
|
||||
dyn_call_jjj,
|
||||
dyn_call_viiij,
|
||||
@ -221,11 +260,20 @@ impl<'a> EmscriptenData<'a> {
|
||||
dyn_call_viiji,
|
||||
dyn_call_viijiii,
|
||||
dyn_call_viijj,
|
||||
dyn_call_vj,
|
||||
dyn_call_vjji,
|
||||
dyn_call_vij,
|
||||
dyn_call_viji,
|
||||
dyn_call_vijiii,
|
||||
dyn_call_vijj,
|
||||
dyn_call_viid,
|
||||
dyn_call_viidii,
|
||||
dyn_call_viidddddddd,
|
||||
temp_ret_0: 0,
|
||||
|
||||
stack_save,
|
||||
stack_restore,
|
||||
set_threw,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -235,22 +283,37 @@ pub fn run_emscripten_instance(
|
||||
instance: &mut Instance,
|
||||
path: &str,
|
||||
args: Vec<&str>,
|
||||
entrypoint: Option<String>,
|
||||
) -> CallResult<()> {
|
||||
let mut data = EmscriptenData::new(instance);
|
||||
let data_ptr = &mut data as *mut _ as *mut c_void;
|
||||
instance.context_mut().data = data_ptr;
|
||||
|
||||
// ATINIT
|
||||
// (used by C++)
|
||||
if let Ok(_func) = instance.dyn_func("globalCtors") {
|
||||
instance.call("globalCtors", &[])?;
|
||||
}
|
||||
|
||||
if let Ok(_func) = instance.dyn_func("___emscripten_environ_constructor") {
|
||||
instance.call("___emscripten_environ_constructor", &[])?;
|
||||
}
|
||||
|
||||
// println!("running emscripten instance");
|
||||
|
||||
if let Some(ep) = entrypoint {
|
||||
debug!("Running entry point: {}", &ep);
|
||||
let arg = unsafe { allocate_cstr_on_stack(instance.context_mut(), args[0]).0 };
|
||||
//let (argc, argv) = store_module_arguments(instance.context_mut(), args);
|
||||
instance.call(&ep, &[Value::I32(arg as i32)])?;
|
||||
} else {
|
||||
let main_func = instance.dyn_func("_main")?;
|
||||
let num_params = main_func.signature().params().len();
|
||||
let _result = match num_params {
|
||||
2 => {
|
||||
let (argc, argv) = store_module_arguments(instance.context_mut(), path, args);
|
||||
let mut new_args = vec![path];
|
||||
new_args.extend(args);
|
||||
let (argc, argv) = store_module_arguments(instance.context_mut(), new_args);
|
||||
instance.call("_main", &[Value::I32(argc as i32), Value::I32(argv as i32)])?;
|
||||
}
|
||||
0 => {
|
||||
@ -261,37 +324,35 @@ pub fn run_emscripten_instance(
|
||||
num_params
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
// TODO atinit and atexit for emscripten
|
||||
// TODO atexit for emscripten
|
||||
// println!("{:?}", data);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn store_module_arguments(ctx: &mut Ctx, path: &str, args: Vec<&str>) -> (u32, u32) {
|
||||
fn store_module_arguments(ctx: &mut Ctx, args: Vec<&str>) -> (u32, u32) {
|
||||
let argc = args.len() + 1;
|
||||
|
||||
let mut args_slice = vec![0; argc];
|
||||
args_slice[0] = unsafe { allocate_cstr_on_stack(ctx, path).0 };
|
||||
for (slot, arg) in args_slice[1..argc].iter_mut().zip(args.iter()) {
|
||||
for (slot, arg) in args_slice[0..argc].iter_mut().zip(args.iter()) {
|
||||
*slot = unsafe { allocate_cstr_on_stack(ctx, &arg).0 };
|
||||
}
|
||||
|
||||
let (argv_offset, argv_slice): (_, &mut [u32]) =
|
||||
unsafe { allocate_on_stack(ctx, ((argc + 1) * 4) as u32) };
|
||||
unsafe { allocate_on_stack(ctx, ((argc) * 4) as u32) };
|
||||
assert!(!argv_slice.is_empty());
|
||||
for (slot, arg) in argv_slice[0..argc].iter_mut().zip(args_slice.iter()) {
|
||||
*slot = *arg
|
||||
}
|
||||
argv_slice[argc] = 0;
|
||||
|
||||
(argc as u32, argv_offset)
|
||||
(argc as u32 - 1, argv_offset)
|
||||
}
|
||||
|
||||
pub fn emscripten_set_up_memory(memory: &Memory, globals: &EmscriptenGlobalsData) {
|
||||
let dynamictop_ptr = globals.dynamictop_ptr;
|
||||
let stack_max = globals.stack_max;
|
||||
|
||||
let dynamic_base = align_memory(stack_max);
|
||||
let dynamic_base = globals.dynamic_base;
|
||||
|
||||
memory.view::<u32>()[(dynamictop_ptr / 4) as usize].set(dynamic_base);
|
||||
}
|
||||
@ -302,6 +363,7 @@ pub struct EmscriptenGlobalsData {
|
||||
stacktop: u32,
|
||||
stack_max: u32,
|
||||
dynamictop_ptr: u32,
|
||||
dynamic_base: u32,
|
||||
memory_base: u32,
|
||||
table_base: u32,
|
||||
temp_double_ptr: u32,
|
||||
@ -371,7 +433,14 @@ impl EmscriptenGlobals {
|
||||
let temp_double_ptr = static_top;
|
||||
static_top += 16;
|
||||
|
||||
let (dynamic_base, dynamictop_ptr) =
|
||||
get_emscripten_metadata(&module).unwrap_or_else(|| {
|
||||
let dynamictop_ptr = static_alloc(&mut static_top, 4);
|
||||
(
|
||||
align_memory(align_memory(static_top) + TOTAL_STACK),
|
||||
dynamictop_ptr,
|
||||
)
|
||||
});
|
||||
|
||||
let stacktop = align_memory(static_top);
|
||||
let stack_max = stacktop + TOTAL_STACK;
|
||||
@ -381,6 +450,7 @@ impl EmscriptenGlobals {
|
||||
stacktop,
|
||||
stack_max,
|
||||
dynamictop_ptr,
|
||||
dynamic_base,
|
||||
memory_base,
|
||||
table_base,
|
||||
temp_double_ptr,
|
||||
@ -476,6 +546,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
|
||||
"_getpagesize" => func!(crate::env::_getpagesize),
|
||||
"_sysconf" => func!(crate::env::_sysconf),
|
||||
"_getaddrinfo" => func!(crate::env::_getaddrinfo),
|
||||
"_times" => func!(crate::env::_times),
|
||||
|
||||
// Syscalls
|
||||
"___syscall1" => func!(crate::syscalls::___syscall1),
|
||||
@ -505,8 +576,8 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
|
||||
"___syscall77" => func!(crate::syscalls::___syscall77),
|
||||
"___syscall83" => func!(crate::syscalls::___syscall83),
|
||||
"___syscall85" => func!(crate::syscalls::___syscall85),
|
||||
"___syscall91" => func!(crate::syscalls::___syscall191),
|
||||
"___syscall94" => func!(crate::syscalls::___syscall194),
|
||||
"___syscall91" => func!(crate::syscalls::___syscall91),
|
||||
"___syscall94" => func!(crate::syscalls::___syscall94),
|
||||
"___syscall97" => func!(crate::syscalls::___syscall97),
|
||||
"___syscall102" => func!(crate::syscalls::___syscall102),
|
||||
"___syscall110" => func!(crate::syscalls::___syscall110),
|
||||
@ -543,6 +614,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
|
||||
"___syscall272" => func!(crate::syscalls::___syscall272),
|
||||
"___syscall295" => func!(crate::syscalls::___syscall295),
|
||||
"___syscall300" => func!(crate::syscalls::___syscall300),
|
||||
"___syscall320" => func!(crate::syscalls::___syscall320),
|
||||
"___syscall324" => func!(crate::syscalls::___syscall324),
|
||||
"___syscall330" => func!(crate::syscalls::___syscall330),
|
||||
"___syscall334" => func!(crate::syscalls::___syscall334),
|
||||
@ -562,8 +634,10 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
|
||||
"_kill" => func!(crate::process::_kill),
|
||||
"_llvm_stackrestore" => func!(crate::process::_llvm_stackrestore),
|
||||
"_llvm_stacksave" => func!(crate::process::_llvm_stacksave),
|
||||
"_llvm_eh_typeid_for" => func!(crate::process::_llvm_eh_typeid_for),
|
||||
"_raise" => func!(crate::process::_raise),
|
||||
"_sem_init" => func!(crate::process::_sem_init),
|
||||
"_sem_destroy" => func!(crate::process::_sem_destroy),
|
||||
"_sem_post" => func!(crate::process::_sem_post),
|
||||
"_sem_wait" => func!(crate::process::_sem_wait),
|
||||
"_getgrent" => func!(crate::process::_getgrent),
|
||||
@ -591,12 +665,19 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
|
||||
"_emscripten_get_heap_size" => func!(crate::memory::_emscripten_get_heap_size),
|
||||
"_emscripten_resize_heap" => func!(crate::memory::_emscripten_resize_heap),
|
||||
"enlargeMemory" => func!(crate::memory::enlarge_memory),
|
||||
"segfault" => func!(crate::memory::segfault),
|
||||
"alignfault" => func!(crate::memory::alignfault),
|
||||
"ftfault" => func!(crate::memory::ftfault),
|
||||
"getTotalMemory" => func!(crate::memory::get_total_memory),
|
||||
"___map_file" => func!(crate::memory::___map_file),
|
||||
|
||||
// Exception
|
||||
"___cxa_allocate_exception" => func!(crate::exception::___cxa_allocate_exception),
|
||||
"___cxa_throw" => func!(crate::exception::___cxa_throw),
|
||||
"___cxa_begin_catch" => func!(crate::exception::___cxa_begin_catch),
|
||||
"___cxa_end_catch" => func!(crate::exception::___cxa_end_catch),
|
||||
"___cxa_uncaught_exception" => func!(crate::exception::___cxa_uncaught_exception),
|
||||
"___cxa_pure_virtual" => func!(crate::exception::___cxa_pure_virtual),
|
||||
|
||||
// Time
|
||||
"_gettimeofday" => func!(crate::time::_gettimeofday),
|
||||
@ -609,6 +690,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
|
||||
"_localtime" => func!(crate::time::_localtime),
|
||||
"_time" => func!(crate::time::_time),
|
||||
"_strftime" => func!(crate::time::_strftime),
|
||||
"_strftime_l" => func!(crate::time::_strftime_l),
|
||||
"_localtime_r" => func!(crate::time::_localtime_r),
|
||||
"_gmtime_r" => func!(crate::time::_gmtime_r),
|
||||
"_mktime" => func!(crate::time::_mktime),
|
||||
@ -622,12 +704,16 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
|
||||
"_llvm_log2_f32" => func!(crate::math::_llvm_log2_f64),
|
||||
"_llvm_sin_f64" => func!(crate::math::_llvm_sin_f64),
|
||||
"_llvm_cos_f64" => func!(crate::math::_llvm_cos_f64),
|
||||
"_llvm_exp2_f32" => func!(crate::math::_llvm_exp2_f32),
|
||||
"_llvm_exp2_f64" => func!(crate::math::_llvm_exp2_f64),
|
||||
"_llvm_trunc_f64" => func!(crate::math::_llvm_trunc_f64),
|
||||
"_emscripten_random" => func!(crate::math::_emscripten_random),
|
||||
|
||||
// Jump
|
||||
"__setjmp" => func!(crate::jmp::__setjmp),
|
||||
"__longjmp" => func!(crate::jmp::__longjmp),
|
||||
"_longjmp" => func!(crate::jmp::__longjmp),
|
||||
"_longjmp" => func!(crate::jmp::_longjmp),
|
||||
"_emscripten_longjmp" => func!(crate::jmp::_longjmp),
|
||||
|
||||
// Bitwise
|
||||
"_llvm_bswap_i64" => func!(crate::bitwise::_llvm_bswap_i64),
|
||||
@ -639,14 +725,18 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
|
||||
"_dlsym" => func!(crate::linking::_dlsym),
|
||||
|
||||
// wasm32-unknown-emscripten
|
||||
"_atexit" => func!(crate::emscripten_target::_atexit),
|
||||
"setTempRet0" => func!(crate::emscripten_target::setTempRet0),
|
||||
"getTempRet0" => func!(crate::emscripten_target::getTempRet0),
|
||||
"invoke_i" => func!(crate::emscripten_target::invoke_i),
|
||||
"invoke_ii" => func!(crate::emscripten_target::invoke_ii),
|
||||
"invoke_iii" => func!(crate::emscripten_target::invoke_iii),
|
||||
"invoke_iiii" => func!(crate::emscripten_target::invoke_iiii),
|
||||
"invoke_iifi" => func!(crate::emscripten_target::invoke_iifi),
|
||||
"invoke_v" => func!(crate::emscripten_target::invoke_v),
|
||||
"invoke_vi" => func!(crate::emscripten_target::invoke_vi),
|
||||
"invoke_vj" => func!(crate::emscripten_target::invoke_vj),
|
||||
"invoke_vjji" => func!(crate::emscripten_target::invoke_vjji),
|
||||
"invoke_vii" => func!(crate::emscripten_target::invoke_vii),
|
||||
"invoke_viii" => func!(crate::emscripten_target::invoke_viii),
|
||||
"invoke_viiii" => func!(crate::emscripten_target::invoke_viiii),
|
||||
@ -659,10 +749,15 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
|
||||
"___resumeException" => func!(crate::emscripten_target::___resumeException),
|
||||
"_dladdr" => func!(crate::emscripten_target::_dladdr),
|
||||
"_pthread_create" => func!(crate::emscripten_target::_pthread_create),
|
||||
"_pthread_detach" => func!(crate::emscripten_target::_pthread_detach),
|
||||
"_pthread_join" => func!(crate::emscripten_target::_pthread_join),
|
||||
"_pthread_cond_destroy" => func!(crate::emscripten_target::_pthread_cond_destroy),
|
||||
"_pthread_attr_init" => func!(crate::emscripten_target::_pthread_attr_init),
|
||||
"_pthread_attr_destroy" => func!(crate::emscripten_target::_pthread_attr_destroy),
|
||||
"_pthread_attr_getstack" => func!(crate::emscripten_target::_pthread_attr_getstack),
|
||||
"_pthread_cond_init" => func!(crate::emscripten_target::_pthread_cond_init),
|
||||
"_pthread_cond_destroy" => func!(crate::emscripten_target::_pthread_cond_destroy),
|
||||
"_pthread_cond_signal" => func!(crate::emscripten_target::_pthread_cond_signal),
|
||||
"_pthread_cond_timedwait" => func!(crate::emscripten_target::_pthread_cond_timedwait),
|
||||
"_pthread_cond_wait" => func!(crate::emscripten_target::_pthread_cond_wait),
|
||||
"_pthread_condattr_destroy" => func!(crate::emscripten_target::_pthread_condattr_destroy),
|
||||
"_pthread_condattr_init" => func!(crate::emscripten_target::_pthread_condattr_init),
|
||||
@ -675,6 +770,11 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
|
||||
"_pthread_rwlock_rdlock" => func!(crate::emscripten_target::_pthread_rwlock_rdlock),
|
||||
"_pthread_rwlock_unlock" => func!(crate::emscripten_target::_pthread_rwlock_unlock),
|
||||
"_pthread_setcancelstate" => func!(crate::emscripten_target::_pthread_setcancelstate),
|
||||
"_pthread_getspecific" => func!(crate::emscripten_target::_pthread_getspecific),
|
||||
"_pthread_getattr_np" => func!(crate::emscripten_target::_pthread_getattr_np),
|
||||
"_pthread_setspecific" => func!(crate::emscripten_target::_pthread_setspecific),
|
||||
"_pthread_once" => func!(crate::emscripten_target::_pthread_once),
|
||||
"_pthread_key_create" => func!(crate::emscripten_target::_pthread_key_create),
|
||||
"___gxx_personality_v0" => func!(crate::emscripten_target::___gxx_personality_v0),
|
||||
"_getdtablesize" => func!(crate::emscripten_target::_getdtablesize),
|
||||
"_gethostbyaddr" => func!(crate::emscripten_target::_gethostbyaddr),
|
||||
@ -686,16 +786,23 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
|
||||
"invoke_iiiiii" => func!(crate::emscripten_target::invoke_iiiiii),
|
||||
"invoke_iiiiiii" => func!(crate::emscripten_target::invoke_iiiiiii),
|
||||
"invoke_iiiiiiii" => func!(crate::emscripten_target::invoke_iiiiiiii),
|
||||
"invoke_iiiiiiiii" => func!(crate::emscripten_target::invoke_iiiiiiiii),
|
||||
"invoke_iiiiiiiiii" => func!(crate::emscripten_target::invoke_iiiiiiiiii),
|
||||
"invoke_iiiiiiiiiii" => func!(crate::emscripten_target::invoke_iiiiiiiiiii),
|
||||
"invoke_vd" => func!(crate::emscripten_target::invoke_vd),
|
||||
"invoke_viiiii" => func!(crate::emscripten_target::invoke_viiiii),
|
||||
"invoke_viiiiii" => func!(crate::emscripten_target::invoke_viiiiii),
|
||||
"invoke_viiiiiii" => func!(crate::emscripten_target::invoke_viiiiiii),
|
||||
"invoke_viiiiiiii" => func!(crate::emscripten_target::invoke_viiiiiiii),
|
||||
"invoke_viiiiiiiii" => func!(crate::emscripten_target::invoke_viiiiiiiii),
|
||||
"invoke_viiiiiiiii" => func!(crate::emscripten_target::invoke_viiiiiiiii),
|
||||
"invoke_viiiiiiiiii" => func!(crate::emscripten_target::invoke_viiiiiiiiii),
|
||||
"invoke_iij" => func!(crate::emscripten_target::invoke_iij),
|
||||
"invoke_iiji" => func!(crate::emscripten_target::invoke_iiji),
|
||||
"invoke_iiijj" => func!(crate::emscripten_target::invoke_iiijj),
|
||||
"invoke_j" => func!(crate::emscripten_target::invoke_j),
|
||||
"invoke_ji" => func!(crate::emscripten_target::invoke_ji),
|
||||
"invoke_jii" => func!(crate::emscripten_target::invoke_jii),
|
||||
"invoke_jij" => func!(crate::emscripten_target::invoke_jij),
|
||||
"invoke_jjj" => func!(crate::emscripten_target::invoke_jjj),
|
||||
"invoke_viiij" => func!(crate::emscripten_target::invoke_viiij),
|
||||
@ -709,7 +816,9 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
|
||||
"invoke_viji" => func!(crate::emscripten_target::invoke_viji),
|
||||
"invoke_vijiii" => func!(crate::emscripten_target::invoke_vijiii),
|
||||
"invoke_vijj" => func!(crate::emscripten_target::invoke_vijj),
|
||||
"invoke_viid" => func!(crate::emscripten_target::invoke_viid),
|
||||
"invoke_viidii" => func!(crate::emscripten_target::invoke_viidii),
|
||||
"invoke_viidddddddd" => func!(crate::emscripten_target::invoke_viidddddddd),
|
||||
};
|
||||
|
||||
for null_func_name in globals.null_func_names.iter() {
|
||||
@ -729,6 +838,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
|
||||
},
|
||||
"asm2wasm" => {
|
||||
"f64-rem" => func!(crate::math::f64_rem),
|
||||
"f64-to-int" => func!(crate::math::f64_to_int),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -34,12 +34,27 @@ pub fn _llvm_log2_f32(_ctx: &mut Ctx, _value: f64) -> f64 {
|
||||
-1.0
|
||||
}
|
||||
|
||||
pub fn _llvm_exp2_f32(_ctx: &mut Ctx, value: f32) -> f32 {
|
||||
debug!("emscripten::_llvm_exp2_f32");
|
||||
2f32.powf(value)
|
||||
}
|
||||
|
||||
pub fn _llvm_exp2_f64(_ctx: &mut Ctx, value: f64) -> f64 {
|
||||
debug!("emscripten::_llvm_exp2_f64");
|
||||
2f64.powf(value)
|
||||
}
|
||||
|
||||
pub fn _llvm_trunc_f64(_ctx: &mut Ctx, value: f64) -> f64 {
|
||||
debug!("emscripten::_llvm_trunc_f64");
|
||||
value.trunc()
|
||||
}
|
||||
|
||||
pub fn _emscripten_random(_ctx: &mut Ctx) -> f64 {
|
||||
debug!("emscripten::_emscripten_random");
|
||||
-1.0
|
||||
}
|
||||
|
||||
// emscripten: f64-rem
|
||||
// emscripten: asm2wasm.f64-rem
|
||||
pub fn f64_rem(_ctx: &mut Ctx, x: f64, y: f64) -> f64 {
|
||||
debug!("emscripten::f64-rem");
|
||||
x % y
|
||||
@ -59,3 +74,9 @@ pub fn exp(_ctx: &mut Ctx, value: f64) -> f64 {
|
||||
pub fn log(_ctx: &mut Ctx, value: f64) -> f64 {
|
||||
value.ln()
|
||||
}
|
||||
|
||||
// emscripten: asm2wasm.f64-to-int
|
||||
pub fn f64_to_int(_ctx: &mut Ctx, value: f64) -> i32 {
|
||||
debug!("emscripten::f64_to_int {}", value);
|
||||
value as i32
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
use super::process::abort_with_message;
|
||||
use libc::{c_int, c_void, memcpy, size_t};
|
||||
use wasmer_runtime_core::vm::Ctx;
|
||||
use wasmer_runtime_core::{
|
||||
units::{Pages, WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE},
|
||||
vm::Ctx,
|
||||
};
|
||||
|
||||
/// emscripten: _emscripten_memcpy_big
|
||||
pub fn _emscripten_memcpy_big(ctx: &mut Ctx, dest: u32, src: u32, len: u32) -> u32 {
|
||||
@ -17,17 +20,49 @@ pub fn _emscripten_memcpy_big(ctx: &mut Ctx, dest: u32, src: u32, len: u32) -> u
|
||||
}
|
||||
|
||||
/// emscripten: _emscripten_get_heap_size
|
||||
pub fn _emscripten_get_heap_size(_ctx: &mut Ctx) -> u32 {
|
||||
debug!("emscripten::_emscripten_get_heap_size",);
|
||||
// TODO: Fix implementation
|
||||
16_777_216
|
||||
pub fn _emscripten_get_heap_size(ctx: &mut Ctx) -> u32 {
|
||||
debug!("emscripten::_emscripten_get_heap_size");
|
||||
let result = ctx.memory(0).size().bytes().0 as u32;
|
||||
debug!("=> {}", result);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
// From emscripten implementation
|
||||
fn align_up(mut val: usize, multiple: usize) -> usize {
|
||||
if val % multiple > 0 {
|
||||
val += multiple - val % multiple;
|
||||
}
|
||||
val
|
||||
}
|
||||
|
||||
/// emscripten: _emscripten_resize_heap
|
||||
pub fn _emscripten_resize_heap(_ctx: &mut Ctx, _requested_size: u32) -> u32 {
|
||||
debug!("emscripten::_emscripten_resize_heap {}", _requested_size);
|
||||
// TODO: Fix implementation
|
||||
/// Note: this function only allows growing the size of heap
|
||||
pub fn _emscripten_resize_heap(ctx: &mut Ctx, requested_size: u32) -> u32 {
|
||||
debug!("emscripten::_emscripten_resize_heap {}", requested_size);
|
||||
let current_memory_pages = ctx.memory(0).size();
|
||||
let current_memory = current_memory_pages.bytes().0 as u32;
|
||||
|
||||
// implementation from emscripten
|
||||
let mut new_size = usize::max(current_memory as usize, WASM_MIN_PAGES * WASM_PAGE_SIZE);
|
||||
while new_size < requested_size as usize {
|
||||
if new_size <= 0x2000_0000 {
|
||||
new_size = align_up(new_size * 2, WASM_PAGE_SIZE);
|
||||
} else {
|
||||
new_size = usize::min(
|
||||
align_up((3 * new_size + 0x8000_0000) / 4, WASM_PAGE_SIZE),
|
||||
WASM_PAGE_SIZE * WASM_MAX_PAGES,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let amount_to_grow = (new_size - current_memory as usize) / WASM_PAGE_SIZE;
|
||||
if let Ok(_pages_allocated) = ctx.memory(0).grow(Pages(amount_to_grow as u32)) {
|
||||
debug!("{} pages allocated", _pages_allocated.0);
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/// emscripten: getTotalMemory
|
||||
@ -35,7 +70,7 @@ pub fn get_total_memory(_ctx: &mut Ctx) -> u32 {
|
||||
debug!("emscripten::get_total_memory");
|
||||
// instance.memories[0].current_pages()
|
||||
// TODO: Fix implementation
|
||||
16_777_216
|
||||
_ctx.memory(0).size().bytes().0 as u32
|
||||
}
|
||||
|
||||
/// emscripten: enlargeMemory
|
||||
@ -63,6 +98,24 @@ pub fn abort_on_cannot_grow_memory_old(ctx: &mut Ctx) -> u32 {
|
||||
0
|
||||
}
|
||||
|
||||
/// emscripten: segfault
|
||||
pub fn segfault(ctx: &mut Ctx) {
|
||||
debug!("emscripten::segfault");
|
||||
abort_with_message(ctx, "segmentation fault");
|
||||
}
|
||||
|
||||
/// emscripten: alignfault
|
||||
pub fn alignfault(ctx: &mut Ctx) {
|
||||
debug!("emscripten::alignfault");
|
||||
abort_with_message(ctx, "alignment fault");
|
||||
}
|
||||
|
||||
/// emscripten: ftfault
|
||||
pub fn ftfault(ctx: &mut Ctx) {
|
||||
debug!("emscripten::ftfault");
|
||||
abort_with_message(ctx, "Function table mask error");
|
||||
}
|
||||
|
||||
/// emscripten: ___map_file
|
||||
pub fn ___map_file(_ctx: &mut Ctx, _one: u32, _two: u32) -> c_int {
|
||||
debug!("emscripten::___map_file");
|
||||
|
@ -82,8 +82,13 @@ pub fn _raise(_ctx: &mut Ctx, _one: i32) -> i32 {
|
||||
}
|
||||
|
||||
pub fn _sem_init(_ctx: &mut Ctx, _one: i32, _two: i32, _three: i32) -> i32 {
|
||||
debug!("emscripten::_sem_init");
|
||||
-1
|
||||
debug!("emscripten::_sem_init: {}, {}, {}", _one, _two, _three);
|
||||
0
|
||||
}
|
||||
|
||||
pub fn _sem_destroy(_ctx: &mut Ctx, _one: i32) -> i32 {
|
||||
debug!("emscripten::_sem_destroy");
|
||||
0
|
||||
}
|
||||
|
||||
pub fn _sem_post(_ctx: &mut Ctx, _one: i32) -> i32 {
|
||||
@ -150,11 +155,16 @@ pub fn _llvm_trap(ctx: &mut Ctx) {
|
||||
abort_with_message(ctx, "abort!");
|
||||
}
|
||||
|
||||
pub fn _llvm_eh_typeid_for(_ctx: &mut Ctx, _type_info_addr: u32) -> i32 {
|
||||
debug!("emscripten::_llvm_eh_typeid_for");
|
||||
-1
|
||||
}
|
||||
|
||||
pub fn _system(_ctx: &mut Ctx, _one: i32) -> c_int {
|
||||
debug!("emscripten::_system");
|
||||
// TODO: May need to change this Em impl to a working version
|
||||
eprintln!("Can't call external programs");
|
||||
return EAGAIN;
|
||||
EAGAIN
|
||||
}
|
||||
|
||||
pub fn _popen(_ctx: &mut Ctx, _one: i32, _two: i32) -> c_int {
|
||||
|
@ -31,12 +31,13 @@ use libc::{
|
||||
off_t,
|
||||
// open,
|
||||
read,
|
||||
rename,
|
||||
// sockaddr_in,
|
||||
// readv,
|
||||
rmdir,
|
||||
// writev,
|
||||
stat,
|
||||
write,
|
||||
// sockaddr_in,
|
||||
};
|
||||
use wasmer_runtime_core::vm::Ctx;
|
||||
|
||||
@ -118,9 +119,21 @@ pub fn ___syscall20(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
|
||||
unsafe { getpid() }
|
||||
}
|
||||
|
||||
pub fn ___syscall38(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
|
||||
debug!("emscripten::___syscall38");
|
||||
-1
|
||||
// rename
|
||||
pub fn ___syscall38(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 {
|
||||
debug!("emscripten::___syscall38 (rename)");
|
||||
let old_path_addr: u32 = varargs.get(ctx);
|
||||
let new_path_addr: u32 = varargs.get(ctx);
|
||||
let old_path = emscripten_memory_pointer!(ctx.memory(0), old_path_addr) as *const i8;
|
||||
let new_path = emscripten_memory_pointer!(ctx.memory(0), new_path_addr) as *const i8;
|
||||
let result = unsafe { rename(old_path, new_path) };
|
||||
debug!(
|
||||
"=> old_path: {}, new_path: {}, result: {}",
|
||||
unsafe { std::ffi::CStr::from_ptr(old_path).to_str().unwrap() },
|
||||
unsafe { std::ffi::CStr::from_ptr(new_path).to_str().unwrap() },
|
||||
result
|
||||
);
|
||||
result
|
||||
}
|
||||
|
||||
// rmdir
|
||||
@ -194,8 +207,8 @@ pub fn ___syscall85(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
|
||||
}
|
||||
|
||||
pub fn ___syscall91(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
|
||||
debug!("emscripten::___syscall91");
|
||||
-1
|
||||
debug!("emscripten::___syscall91 - stub");
|
||||
0
|
||||
}
|
||||
|
||||
pub fn ___syscall97(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
|
||||
@ -246,12 +259,21 @@ pub fn ___syscall192(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
|
||||
if fd == -1 {
|
||||
let ptr = env::call_memalign(ctx, 16384, len);
|
||||
if ptr == 0 {
|
||||
return -1;
|
||||
// ENOMEM
|
||||
return -12;
|
||||
}
|
||||
let real_ptr = emscripten_memory_pointer!(ctx.memory(0), ptr) as *const u8;
|
||||
env::call_memset(ctx, ptr, 0, len);
|
||||
ptr as _
|
||||
for i in 0..(len as usize) {
|
||||
unsafe {
|
||||
assert_eq!(*real_ptr.add(i), 0);
|
||||
}
|
||||
}
|
||||
debug!("=> ptr: {}", ptr);
|
||||
return ptr as i32;
|
||||
} else {
|
||||
-1
|
||||
// return ENODEV
|
||||
return -19;
|
||||
}
|
||||
}
|
||||
|
||||
@ -461,6 +483,12 @@ pub fn ___syscall300(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
|
||||
-1
|
||||
}
|
||||
|
||||
// utimensat
|
||||
pub fn ___syscall320(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_int {
|
||||
debug!("emscripten::___syscall320 (utimensat), {}", _which);
|
||||
0
|
||||
}
|
||||
|
||||
pub fn ___syscall334(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
|
||||
debug!("emscripten::___syscall334");
|
||||
-1
|
||||
|
@ -1,4 +1,6 @@
|
||||
use crate::varargs::VarArgs;
|
||||
#[cfg(target_os = "macos")]
|
||||
use libc::size_t;
|
||||
/// NOTE: TODO: These syscalls only support wasm_32 for now because they assume offsets are u32
|
||||
/// Syscall list: https://www.cs.utexas.edu/~bismith/test/syscalls/syscalls32.html
|
||||
use libc::{
|
||||
@ -53,10 +55,10 @@ use libc::{
|
||||
sendto,
|
||||
setpgid,
|
||||
setsockopt,
|
||||
size_t,
|
||||
sockaddr,
|
||||
socket,
|
||||
socklen_t,
|
||||
stat,
|
||||
symlink,
|
||||
uid_t,
|
||||
uname,
|
||||
@ -73,6 +75,7 @@ use libc::{
|
||||
};
|
||||
use wasmer_runtime_core::vm::Ctx;
|
||||
|
||||
use crate::utils;
|
||||
#[allow(unused_imports)]
|
||||
use std::io::Error;
|
||||
use std::mem;
|
||||
@ -88,7 +91,7 @@ extern "C" {
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
use libc::{fallocate, fdatasync, ftruncate64, lstat64, madvise, wait4};
|
||||
use libc::{fallocate, fdatasync, ftruncate64, lstat, madvise, wait4};
|
||||
|
||||
// Another conditional constant for name resolution: Macos et iOS use
|
||||
// SO_NOSIGPIPE as a setsockopt flag to disable SIGPIPE emission on socket.
|
||||
@ -249,8 +252,9 @@ pub fn ___syscall33(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int
|
||||
let path = emscripten_memory_pointer!(ctx.memory(0), path_ptr) as *const i8;
|
||||
let result = unsafe { access(path, amode) };
|
||||
debug!(
|
||||
"=> path: {}, result: {}",
|
||||
"=> path: {}, amode: {}, result: {}",
|
||||
unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() },
|
||||
amode,
|
||||
result
|
||||
);
|
||||
result
|
||||
@ -352,8 +356,13 @@ pub fn ___syscall54(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int
|
||||
debug!("emscripten::___syscall54 (ioctl) {}", _which);
|
||||
let fd: i32 = varargs.get(ctx);
|
||||
let request: u32 = varargs.get(ctx);
|
||||
debug!("fd: {}, op: {}", fd, request);
|
||||
debug!("=> fd: {}, op: {}", fd, request);
|
||||
// Got the equivalents here: https://code.woboq.org/linux/linux/include/uapi/asm-generic/ioctls.h.html
|
||||
// let argp: u32 = varargs.get(ctx);
|
||||
// let argp_ptr = emscripten_memory_pointer!(ctx.memory(0), argp) as *mut c_void;
|
||||
// let ret = unsafe { ioctl(fd, request as _, argp_ptr) };
|
||||
// debug!("=> {}", ret);
|
||||
// ret
|
||||
match request as _ {
|
||||
21537 => {
|
||||
// FIONBIO
|
||||
@ -763,19 +772,28 @@ pub fn ___syscall122(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
|
||||
pub fn ___syscall196(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 {
|
||||
debug!("emscripten::___syscall196 (lstat64) {}", _which);
|
||||
let path_ptr: c_int = varargs.get(ctx);
|
||||
let buf_ptr: c_int = varargs.get(ctx);
|
||||
let path = emscripten_memory_pointer!(ctx.memory(0), path_ptr) as *const c_char;
|
||||
let buf = emscripten_memory_pointer!(ctx.memory(0), buf_ptr) as *mut c_void;
|
||||
let result = unsafe { lstat64(path, buf as _) };
|
||||
debug!(
|
||||
"=> path: {}, buf: {} = fd: {}\npath: {}\nlast os error: {}",
|
||||
path_ptr,
|
||||
buf_ptr,
|
||||
result,
|
||||
unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() },
|
||||
Error::last_os_error(),
|
||||
);
|
||||
result
|
||||
let buf_ptr: u32 = varargs.get(ctx);
|
||||
let path = emscripten_memory_pointer!(ctx.memory(0), path_ptr) as *const i8;
|
||||
unsafe {
|
||||
let mut stat: stat = std::mem::zeroed();
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
let stat_ptr = &mut stat as *mut stat as *mut c_void;
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
let stat_ptr = &mut stat as *mut stat;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
let ret = lstat64(path, stat_ptr);
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
let ret = lstat(path, stat_ptr);
|
||||
|
||||
debug!("ret: {}", ret);
|
||||
if ret != 0 {
|
||||
return ret;
|
||||
}
|
||||
utils::copy_stat_into_wasm(ctx, buf_ptr, &stat);
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
/// fallocate
|
||||
|
@ -28,7 +28,9 @@ use wasmer_runtime_core::vm::Ctx;
|
||||
use libc::{CLOCK_MONOTONIC, CLOCK_MONOTONIC_COARSE, CLOCK_REALTIME};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use libc::{CLOCK_MONOTONIC, CLOCK_REALTIME};
|
||||
use libc::CLOCK_REALTIME;
|
||||
#[cfg(target_os = "macos")]
|
||||
const CLOCK_MONOTONIC: clockid_t = 1;
|
||||
#[cfg(target_os = "macos")]
|
||||
const CLOCK_MONOTONIC_COARSE: clockid_t = 6;
|
||||
|
||||
@ -298,15 +300,77 @@ pub fn _time(ctx: &mut Ctx, time_p: u32) -> i32 {
|
||||
|
||||
/// emscripten: _strftime
|
||||
pub fn _strftime(
|
||||
_ctx: &mut Ctx,
|
||||
_s_ptr: c_int,
|
||||
_maxsize: u32,
|
||||
_format_ptr: c_int,
|
||||
_tm_ptr: c_int,
|
||||
ctx: &mut Ctx,
|
||||
s_ptr: c_int,
|
||||
maxsize: u32,
|
||||
format_ptr: c_int,
|
||||
tm_ptr: c_int,
|
||||
) -> i32 {
|
||||
debug!(
|
||||
"emscripten::_strftime {} {} {} {}",
|
||||
_s_ptr, _maxsize, _format_ptr, _tm_ptr
|
||||
s_ptr, maxsize, format_ptr, tm_ptr
|
||||
);
|
||||
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let s = emscripten_memory_pointer!(ctx.memory(0), s_ptr) as *mut c_char;
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let format = emscripten_memory_pointer!(ctx.memory(0), format_ptr) as *const c_char;
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let tm = emscripten_memory_pointer!(ctx.memory(0), tm_ptr) as *const guest_tm;
|
||||
|
||||
let format_string = unsafe { std::ffi::CStr::from_ptr(format).to_str().unwrap() };
|
||||
|
||||
debug!("=> format_string: {:?}", format_string);
|
||||
|
||||
let tm = unsafe { &*tm };
|
||||
|
||||
let rust_tm = ::time::Tm {
|
||||
tm_sec: tm.tm_sec,
|
||||
tm_min: tm.tm_min,
|
||||
tm_hour: tm.tm_hour,
|
||||
tm_mday: tm.tm_mday,
|
||||
tm_mon: tm.tm_mon,
|
||||
tm_year: tm.tm_year,
|
||||
tm_wday: tm.tm_wday,
|
||||
tm_yday: tm.tm_yday,
|
||||
tm_isdst: tm.tm_isdst,
|
||||
tm_utcoff: tm.tm_gmtoff,
|
||||
tm_nsec: 0,
|
||||
};
|
||||
|
||||
let result_str = match ::time::strftime(format_string, &rust_tm) {
|
||||
Ok(res_string) => res_string,
|
||||
// TODO: maybe match on e in Err(e) and return different values if required
|
||||
_ => return 0,
|
||||
};
|
||||
|
||||
// pad for null?
|
||||
let bytes = result_str.chars().count();
|
||||
if bytes as u32 > maxsize {
|
||||
0
|
||||
} else {
|
||||
// write output string
|
||||
for (i, c) in result_str.chars().enumerate() {
|
||||
unsafe { *s.add(i) = c as c_char };
|
||||
}
|
||||
// null terminate?
|
||||
bytes as i32
|
||||
}
|
||||
}
|
||||
|
||||
/// emscripten: _strftime_l
|
||||
pub fn _strftime_l(
|
||||
ctx: &mut Ctx,
|
||||
s_ptr: c_int,
|
||||
maxsize: u32,
|
||||
format_ptr: c_int,
|
||||
tm_ptr: c_int,
|
||||
_last: c_int,
|
||||
) -> i32 {
|
||||
debug!(
|
||||
"emscripten::_strftime_l {} {} {} {}",
|
||||
s_ptr, maxsize, format_ptr, tm_ptr
|
||||
);
|
||||
|
||||
_strftime(ctx, s_ptr, maxsize, format_ptr, tm_ptr)
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use super::env;
|
||||
use super::env::get_emscripten_data;
|
||||
use crate::storage::align_memory;
|
||||
use libc::stat;
|
||||
use std::ffi::CStr;
|
||||
use std::mem::size_of;
|
||||
@ -39,6 +40,43 @@ pub fn get_emscripten_memory_size(module: &Module) -> (Pages, Option<Pages>) {
|
||||
(memory.minimum, memory.maximum)
|
||||
}
|
||||
|
||||
/// Reads values written by `-s EMIT_EMSCRIPTEN_METADATA=1`
|
||||
/// Assumes values start from the end in this order:
|
||||
/// Last export: Dynamic Base
|
||||
/// Second-to-Last export: Dynamic top pointer
|
||||
pub fn get_emscripten_metadata(module: &Module) -> Option<(u32, u32)> {
|
||||
let max_idx = &module.info().globals.iter().map(|(k, _)| k).max()?;
|
||||
let snd_max_idx = &module
|
||||
.info()
|
||||
.globals
|
||||
.iter()
|
||||
.map(|(k, _)| k)
|
||||
.filter(|k| k != max_idx)
|
||||
.max()?;
|
||||
|
||||
use wasmer_runtime_core::types::{GlobalInit, Initializer::Const, Value::I32};
|
||||
if let (
|
||||
GlobalInit {
|
||||
init: Const(I32(dynamic_base)),
|
||||
..
|
||||
},
|
||||
GlobalInit {
|
||||
init: Const(I32(dynamictop_ptr)),
|
||||
..
|
||||
},
|
||||
) = (
|
||||
&module.info().globals[*max_idx],
|
||||
&module.info().globals[*snd_max_idx],
|
||||
) {
|
||||
Some((
|
||||
align_memory(*dynamic_base as u32 - 32),
|
||||
align_memory(*dynamictop_ptr as u32 - 32),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn write_to_buf(ctx: &mut Ctx, string: *const c_char, buf: u32, max: u32) -> u32 {
|
||||
let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as *mut c_char;
|
||||
|
||||
@ -125,7 +163,7 @@ pub struct GuestStat {
|
||||
st_atime: u64,
|
||||
st_mtime: u64,
|
||||
st_ctime: u64,
|
||||
st_ino: u64,
|
||||
st_ino: u32,
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
@ -186,13 +224,13 @@ mod tests {
|
||||
LLVMCompiler::new()
|
||||
}
|
||||
|
||||
#[cfg(feature = "dynasm")]
|
||||
#[cfg(feature = "singlepass")]
|
||||
fn get_compiler() -> impl Compiler {
|
||||
use wasmer_dynasm_backend::SinglePassCompiler;
|
||||
use wasmer_singlepass_backend::SinglePassCompiler;
|
||||
SinglePassCompiler::new()
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "llvm", feature = "clif", feature = "dynasm")))]
|
||||
#[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))]
|
||||
fn get_compiler() -> impl Compiler {
|
||||
panic!("compiler not specified, activate a compiler via features");
|
||||
use wasmer_clif_backend::CraneliftCompiler;
|
||||
|
@ -1,8 +1,5 @@
|
||||
use std::mem;
|
||||
use wasmer_runtime_core::{
|
||||
types::{Type, WasmExternType},
|
||||
vm::Ctx,
|
||||
};
|
||||
use wasmer_runtime_core::{types::WasmExternType, vm::Ctx};
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Copy, Clone)]
|
||||
@ -19,5 +16,12 @@ impl VarArgs {
|
||||
}
|
||||
|
||||
unsafe impl WasmExternType for VarArgs {
|
||||
const TYPE: Type = Type::I32;
|
||||
type Native = i32;
|
||||
|
||||
fn to_native(self) -> Self::Native {
|
||||
self.pointer as _
|
||||
}
|
||||
fn from_native(n: Self::Native) -> Self {
|
||||
Self { pointer: n as u32 }
|
||||
}
|
||||
}
|
||||
|
@ -20,13 +20,13 @@ macro_rules! assert_emscripten_output {
|
||||
LLVMCompiler::new()
|
||||
}
|
||||
|
||||
#[cfg(feature = "dynasm")]
|
||||
#[cfg(feature = "singlepass")]
|
||||
fn get_compiler() -> impl Compiler {
|
||||
use wasmer_dynasm_backend::SinglePassCompiler;
|
||||
use wasmer_singlepass_backend::SinglePassCompiler;
|
||||
SinglePassCompiler::new()
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "llvm", feature = "clif", feature = "dynasm")))]
|
||||
#[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))]
|
||||
fn get_compiler() -> impl Compiler {
|
||||
panic!("compiler not specified, activate a compiler via features");
|
||||
use wasmer_clif_backend::CraneliftCompiler;
|
||||
@ -53,6 +53,7 @@ macro_rules! assert_emscripten_output {
|
||||
&mut instance,
|
||||
$name,
|
||||
$args,
|
||||
None,
|
||||
).expect("run_emscripten_instance finishes");
|
||||
|
||||
let output = capturer.end().unwrap().0;
|
||||
@ -67,36 +68,36 @@ macro_rules! assert_emscripten_output {
|
||||
}};
|
||||
}
|
||||
|
||||
pub fn assert_emscripten_output(wasm_bytes: &[u8], raw_expected_str: &str) {
|
||||
use wasmer_clif_backend::CraneliftCompiler;
|
||||
use wasmer_emscripten::{generate_emscripten_env, stdio::StdioCapturer, EmscriptenGlobals};
|
||||
// pub fn assert_emscripten_output(wasm_bytes: &[u8], raw_expected_str: &str) {
|
||||
// use wasmer_clif_backend::CraneliftCompiler;
|
||||
// use wasmer_emscripten::{generate_emscripten_env, stdio::StdioCapturer, EmscriptenGlobals};
|
||||
|
||||
let module = wasmer_runtime_core::compile_with(&wasm_bytes[..], &CraneliftCompiler::new())
|
||||
.expect("WASM can't be compiled");
|
||||
// let module = wasmer_runtime_core::compile_with(&wasm_bytes[..], &CraneliftCompiler::new())
|
||||
// .expect("WASM can't be compiled");
|
||||
|
||||
let mut emscripten_globals = EmscriptenGlobals::new(&module);
|
||||
let import_object = generate_emscripten_env(&mut emscripten_globals);
|
||||
let mut instance = module
|
||||
.instantiate(&import_object)
|
||||
.map_err(|err| format!("Can't instantiate the WebAssembly module: {:?}", err))
|
||||
.unwrap();
|
||||
// let mut emscripten_globals = EmscriptenGlobals::new(&module);
|
||||
// let import_object = generate_emscripten_env(&mut emscripten_globals);
|
||||
// let mut instance = module
|
||||
// .instantiate(&import_object)
|
||||
// .map_err(|err| format!("Can't instantiate the WebAssembly module: {:?}", err))
|
||||
// .unwrap();
|
||||
|
||||
let capturer = StdioCapturer::new();
|
||||
// let capturer = StdioCapturer::new();
|
||||
|
||||
wasmer_emscripten::run_emscripten_instance(&module, &mut instance, "test", vec![])
|
||||
.expect("run_emscripten_instance finishes");
|
||||
// wasmer_emscripten::run_emscripten_instance(&module, &mut instance, "test", vec![])
|
||||
// .expect("run_emscripten_instance finishes");
|
||||
|
||||
let raw_output_string = capturer.end().unwrap().0;
|
||||
// let raw_output_string = capturer.end().unwrap().0;
|
||||
|
||||
// trim the strings to avoid cross-platform line ending and white space issues
|
||||
let output = raw_output_string.trim();
|
||||
let expected_output = raw_expected_str.trim();
|
||||
// // trim the strings to avoid cross-platform line ending and white space issues
|
||||
// let output = raw_output_string.trim();
|
||||
// let expected_output = raw_expected_str.trim();
|
||||
|
||||
let contains_output = output.contains(expected_output);
|
||||
// let contains_output = output.contains(expected_output);
|
||||
|
||||
assert!(
|
||||
contains_output,
|
||||
"Output: `{}` does not contain expected output: `{}`",
|
||||
output, expected_output
|
||||
);
|
||||
}
|
||||
// assert!(
|
||||
// contains_output,
|
||||
// "Output: `{}` does not contain expected output: `{}`",
|
||||
// output, expected_output
|
||||
// );
|
||||
// }
|
||||
|
@ -176,6 +176,7 @@ mod test_unary_literal;
|
||||
mod test_utf;
|
||||
mod test_varargs;
|
||||
mod test_varargs_multi;
|
||||
mod test_vfs;
|
||||
mod test_vprintf;
|
||||
mod test_vsnprintf;
|
||||
mod test_wprintf;
|
||||
|
@ -1,17 +1,10 @@
|
||||
#[test]
|
||||
fn test_execvp() {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[ignore]
|
||||
fn test_test_execvp() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_execvp.wasm",
|
||||
"test_execvp",
|
||||
vec![],
|
||||
"../../emtests/test_execvp.out"
|
||||
);
|
||||
#[cfg(target_os = "windows")]
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_execvp_windows.wasm",
|
||||
"test_execvp",
|
||||
vec![],
|
||||
"../../emtests/test_execvp.out"
|
||||
);
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
#[test]
|
||||
fn test_getcwd() {
|
||||
fn test_test_getcwd() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_getcwd.wasm",
|
||||
"getcwd",
|
||||
"test_getcwd",
|
||||
vec![],
|
||||
"../../emtests/test_getcwd.out"
|
||||
);
|
||||
|
@ -1,5 +1,4 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_longjmp() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_longjmp.wasm",
|
||||
|
@ -1,5 +1,4 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_longjmp2() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_longjmp2.wasm",
|
||||
|
@ -1,5 +1,4 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_longjmp3() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_longjmp3.wasm",
|
||||
|
@ -1,5 +1,4 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_longjmp4() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_longjmp4.wasm",
|
||||
|
@ -1,5 +1,4 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_longjmp_funcptr() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_longjmp_funcptr.wasm",
|
||||
|
@ -1,5 +1,4 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_longjmp_repeat() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_longjmp_repeat.wasm",
|
||||
|
@ -1,5 +1,4 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_longjmp_stacked() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_longjmp_stacked.wasm",
|
||||
|
@ -1,5 +1,4 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_longjmp_throw() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_longjmp_throw.wasm",
|
||||
|
@ -1,5 +1,4 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_longjmp_unwind() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_longjmp_unwind.wasm",
|
||||
|
@ -1,4 +1,5 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_nl_types() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_nl_types.wasm",
|
||||
|
@ -1,4 +1,5 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_phiundef() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_phiundef.wasm",
|
||||
|
@ -1,8 +1,10 @@
|
||||
use crate::emtests::_common::assert_emscripten_output;
|
||||
|
||||
#[test]
|
||||
fn test_pipe() {
|
||||
let wasm_bytes = include_bytes!("../../emtests/test_pipe.wasm");
|
||||
let expected_str = include_str!("../../emtests/test_pipe.out");
|
||||
assert_emscripten_output(wasm_bytes, expected_str);
|
||||
#[ignore]
|
||||
fn test_test_pipe() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_pipe.wasm",
|
||||
"test_pipe",
|
||||
vec![],
|
||||
"../../emtests/test_pipe.out"
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_printf_2() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_printf_2.wasm",
|
||||
|
@ -1,4 +1,5 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_printf_more() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_printf_more.wasm",
|
||||
|
@ -1,4 +1,5 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_regex() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_regex.wasm",
|
||||
|
@ -1,4 +1,5 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_relocatable_void_function() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_relocatable_void_function.wasm",
|
||||
|
@ -1,4 +1,5 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_rounding() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_rounding.wasm",
|
||||
|
@ -1,4 +1,5 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_set_align() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_set_align.wasm",
|
||||
|
@ -1,4 +1,5 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_sintvars() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_sintvars.wasm",
|
||||
|
@ -1,4 +1,5 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_sizeof() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_sizeof.wasm",
|
||||
|
@ -1,4 +1,5 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_sscanf() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_sscanf.wasm",
|
||||
|
@ -1,4 +1,5 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_sscanf_3() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_sscanf_3.wasm",
|
||||
|
@ -1,4 +1,5 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_sscanf_4() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_sscanf_4.wasm",
|
||||
|
@ -1,4 +1,5 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_sscanf_5() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_sscanf_5.wasm",
|
||||
|
@ -1,4 +1,5 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_sscanf_6() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_sscanf_6.wasm",
|
||||
|
@ -1,4 +1,5 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_sscanf_caps() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_sscanf_caps.wasm",
|
||||
|
@ -1,4 +1,5 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_test_sscanf_float() {
|
||||
assert_emscripten_output!(
|
||||
"../../emtests/test_sscanf_float.wasm",
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user