Merge remote-tracking branch 'origin/master' into middleware

This commit is contained in:
losfair
2020-06-13 00:55:47 +08:00
105 changed files with 3100 additions and 1137 deletions

243
Cargo.lock generated
View File

@@ -148,6 +148,24 @@ dependencies = [
"digest",
]
[[package]]
name = "bstr"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31accafdb70df7871592c058eca3985b71104e15ac32f64706022c58867da931"
dependencies = [
"lazy_static",
"memchr",
"regex-automata",
"serde",
]
[[package]]
name = "bumpalo"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"
[[package]]
name = "byteorder"
version = "1.3.4"
@@ -326,6 +344,41 @@ dependencies = [
"target-lexicon",
]
[[package]]
name = "criterion"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63f696897c88b57f4ffe3c69d8e1a0613c7d0e6c4833363c8560fbde9c47b966"
dependencies = [
"atty",
"cast",
"clap",
"criterion-plot",
"csv",
"itertools",
"lazy_static",
"num-traits",
"oorandom",
"plotters",
"rayon",
"regex",
"serde",
"serde_derive",
"serde_json",
"tinytemplate",
"walkdir",
]
[[package]]
name = "criterion-plot"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddeaf7989f00f2e1d871a26a110f3ed713632feac17f65f03ca938c542618b60"
dependencies = [
"cast",
"itertools",
]
[[package]]
name = "crossbeam-deque"
version = "0.7.3"
@@ -383,6 +436,28 @@ dependencies = [
"subtle",
]
[[package]]
name = "csv"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00affe7f6ab566df61b4be3ce8cf16bc2576bca0963ceb0955e45d514bf9a279"
dependencies = [
"bstr",
"csv-core",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "csv-core"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
dependencies = [
"memchr",
]
[[package]]
name = "ctor"
version = "0.1.14"
@@ -765,6 +840,15 @@ version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
[[package]]
name = "js-sys"
version = "0.3.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce10c23ad2ea25ceca0093bd3192229da4c5b3c0f2de499c1ecac0d98d452177"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@@ -1021,6 +1105,12 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d"
[[package]]
name = "oorandom"
version = "11.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94af325bc33c7f60191be4e2c984d48aaa21e2854f473b85398344b60c9b6358"
[[package]]
name = "orbclient"
version = "0.3.27"
@@ -1104,6 +1194,18 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
[[package]]
name = "plotters"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d1685fbe7beba33de0330629da9d955ac75bd54f33d7b79f9a895590124f6bb"
dependencies = [
"js-sys",
"num-traits",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "ppv-lite86"
version = "0.2.8"
@@ -1390,6 +1492,15 @@ dependencies = [
"thread_local",
]
[[package]]
name = "regex-automata"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4"
dependencies = [
"byteorder",
]
[[package]]
name = "regex-syntax"
version = "0.6.18"
@@ -1444,6 +1555,15 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scoped-tls"
version = "1.0.0"
@@ -1685,7 +1805,7 @@ dependencies = [
[[package]]
name = "test-utils"
version = "0.16.2"
version = "1.0.0-alpha.1"
dependencies = [
"wasmer",
"wasmer-compiler",
@@ -1743,6 +1863,16 @@ dependencies = [
"winapi",
]
[[package]]
name = "tinytemplate"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d3dc76004a03cec1c5932bca4cdc2e39aaa798e3f82363dd94f9adf6098c12f"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "toml"
version = "0.5.6"
@@ -1855,15 +1985,80 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "walkdir"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
dependencies = [
"same-file",
"winapi",
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasm-bindgen"
version = "0.2.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c2dc4aa152834bc334f506c1a06b866416a8b6697d5c9f75b9a689c8486def0"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ded84f06e0ed21499f6184df0e0cb3494727b0c5da89534e0fcc55c51d812101"
dependencies = [
"bumpalo",
"lazy_static",
"log",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "838e423688dac18d73e31edce74ddfac468e37b1506ad163ffaf0a46f703ffe3"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3156052d8ec77142051a533cdd686cba889537b213f948cd1d20869926e68e92"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9ba19973a58daf4db6f352eda73dc0e289493cd29fb2632eb172085b6521acd"
[[package]]
name = "wasm-common"
version = "0.16.2"
version = "1.0.0-alpha.1"
dependencies = [
"cranelift-entity",
"serde",
@@ -1871,7 +2066,7 @@ dependencies = [
[[package]]
name = "wasmer"
version = "0.16.2"
version = "1.0.0-alpha.1"
dependencies = [
"anyhow",
"cfg-if",
@@ -1897,7 +2092,7 @@ dependencies = [
[[package]]
name = "wasmer-bin"
version = "0.16.2"
version = "1.0.0-alpha.1"
dependencies = [
"anyhow",
"atty",
@@ -1905,8 +2100,10 @@ dependencies = [
"bytesize",
"cfg-if",
"colored",
"criterion",
"distance",
"glob",
"lazy_static",
"rustc_version",
"structopt",
"test-generator",
@@ -1929,7 +2126,7 @@ dependencies = [
[[package]]
name = "wasmer-c-api"
version = "0.16.2"
version = "1.0.0-alpha.1"
dependencies = [
"cbindgen",
"cfg-if",
@@ -1945,7 +2142,7 @@ dependencies = [
[[package]]
name = "wasmer-cache"
version = "0.16.2"
version = "1.0.0-alpha.1"
dependencies = [
"blake3",
"hex",
@@ -1956,7 +2153,7 @@ dependencies = [
[[package]]
name = "wasmer-compiler"
version = "0.16.2"
version = "1.0.0-alpha.1"
dependencies = [
"enumset",
"hashbrown",
@@ -1973,7 +2170,7 @@ dependencies = [
[[package]]
name = "wasmer-compiler-cranelift"
version = "0.16.2"
version = "1.0.0-alpha.1"
dependencies = [
"cranelift-codegen",
"cranelift-frontend",
@@ -1991,7 +2188,7 @@ dependencies = [
[[package]]
name = "wasmer-compiler-llvm"
version = "0.16.2"
version = "1.0.0-alpha.1"
dependencies = [
"byteorder",
"cc",
@@ -2013,7 +2210,7 @@ dependencies = [
[[package]]
name = "wasmer-compiler-singlepass"
version = "0.16.2"
version = "1.0.0-alpha.1"
dependencies = [
"byteorder",
"dynasm",
@@ -2031,7 +2228,7 @@ dependencies = [
[[package]]
name = "wasmer-engine"
version = "0.16.2"
version = "1.0.0-alpha.1"
dependencies = [
"backtrace",
"bincode",
@@ -2051,7 +2248,7 @@ dependencies = [
[[package]]
name = "wasmer-engine-dummy"
version = "0.16.2"
version = "1.0.0-alpha.1"
dependencies = [
"bincode",
"serde",
@@ -2064,7 +2261,7 @@ dependencies = [
[[package]]
name = "wasmer-engine-jit"
version = "0.16.2"
version = "1.0.0-alpha.1"
dependencies = [
"bincode",
"region",
@@ -2079,7 +2276,7 @@ dependencies = [
[[package]]
name = "wasmer-engine-native"
version = "0.16.2"
version = "1.0.0-alpha.1"
dependencies = [
"bincode",
"cfg-if",
@@ -2098,7 +2295,7 @@ dependencies = [
[[package]]
name = "wasmer-runtime"
version = "0.16.2"
version = "1.0.0-alpha.1"
dependencies = [
"backtrace",
"cc",
@@ -2116,7 +2313,7 @@ dependencies = [
[[package]]
name = "wasmer-wasi"
version = "0.16.2"
version = "1.0.0-alpha.1"
dependencies = [
"bincode",
"byteorder",
@@ -2134,7 +2331,7 @@ dependencies = [
[[package]]
name = "wasmer-wasi-experimental-io-devices"
version = "0.16.2"
version = "1.0.0-alpha.1"
dependencies = [
"minifb",
"ref_thread_local",
@@ -2146,7 +2343,7 @@ dependencies = [
[[package]]
name = "wasmer-wast"
version = "0.16.2"
version = "1.0.0-alpha.1"
dependencies = [
"anyhow",
"thiserror",
@@ -2248,6 +2445,16 @@ dependencies = [
"dlib",
]
[[package]]
name = "web-sys"
version = "0.3.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b72fe77fd39e4bd3eaa4412fd299a0be6b3dfe9d2597e2f1c20beb968f41d17"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "which"
version = "3.1.1"

View File

@@ -1,6 +1,6 @@
[package]
name = "wasmer-bin"
version = "0.16.2"
version = "1.0.0-alpha.1"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
repository = "https://github.com/wasmerio/wasmer"
description = "High-Performance WebAssembly Framework"
@@ -25,20 +25,20 @@ path = "src/bin/wasmer.rs"
doc = false
[dependencies]
wasmer = { version = "0.16.2", path = "lib/api", default-features = false }
wasmer-compiler = { version = "0.16.2", path = "lib/compiler" }
wasmer-compiler-cranelift = { version = "0.16.2", path = "lib/compiler-cranelift", optional = true }
wasmer-compiler-singlepass = { version = "0.16.2", path = "lib/compiler-singlepass", optional = true }
wasmer-compiler-llvm = { version = "0.16.2", path = "lib/compiler-llvm", optional = true }
#wasmer-emscripten = { version = "0.16.2", path = "lib/emscripten", optional = true }
wasmer-engine = { version = "0.16.2", path = "lib/engine" }
wasmer-engine-jit = { version = "0.16.2", path = "lib/engine-jit", optional = true }
wasmer-engine-native = { version = "0.16.2", path = "lib/engine-native", optional = true }
wasmer-wasi = { version = "0.16.2", path = "lib/wasi", optional = true }
wasmer-wasi-experimental-io-devices = { version = "0.16.2", path = "lib/wasi-experimental-io-devices", optional = true }
wasmer-wast = { version = "0.16.2", path = "tests/lib/wast", optional = true }
wasmer-cache = { version = "0.16.2", path = "lib/cache", optional = true }
wasm-common = { version = "0.16.2", path = "lib/wasm-common" }
wasmer = { version = "1.0.0-alpha.1", path = "lib/api", default-features = false }
wasmer-compiler = { version = "1.0.0-alpha.1", path = "lib/compiler" }
wasmer-compiler-cranelift = { version = "1.0.0-alpha.1", path = "lib/compiler-cranelift", optional = true }
wasmer-compiler-singlepass = { version = "1.0.0-alpha.1", path = "lib/compiler-singlepass", optional = true }
wasmer-compiler-llvm = { version = "1.0.0-alpha.1", path = "lib/compiler-llvm", optional = true }
#wasmer-emscripten = { version = "1.0.0-alpha.1", path = "lib/emscripten", optional = true }
wasmer-engine = { version = "1.0.0-alpha.1", path = "lib/engine" }
wasmer-engine-jit = { version = "1.0.0-alpha.1", path = "lib/engine-jit", optional = true }
wasmer-engine-native = { version = "1.0.0-alpha.1", path = "lib/engine-native", optional = true }
wasmer-wasi = { version = "1.0.0-alpha.1", path = "lib/wasi", optional = true }
wasmer-wasi-experimental-io-devices = { version = "1.0.0-alpha.1", path = "lib/wasi-experimental-io-devices", optional = true }
wasmer-wast = { version = "1.0.0-alpha.1", path = "tests/lib/wast", optional = true }
wasmer-cache = { version = "1.0.0-alpha.1", path = "lib/cache", optional = true }
wasm-common = { version = "1.0.0-alpha.1", path = "lib/wasm-common" }
atty = "0.2"
colored = "1.9"
anyhow = "1.0"
@@ -63,6 +63,8 @@ rustc_version = "0.2"
[dev-dependencies]
anyhow = "1.0"
blake3 = "0.3"
criterion = "0.3"
lazy_static = "1.4"
test-utils = { path = "tests/lib/test-utils" }
wasmer-engine-dummy = { path = "tests/lib/engine-dummy" }
@@ -105,5 +107,20 @@ llvm = [
"compiler",
]
# Testing features
test-singlepass = [
"singlepass",
]
test-cranelift = [
"cranelift",
]
test-llvm = [
"llvm",
]
# [profile.release]
# lto = "fat"
[[bench]]
name = "static_and_dynamic_functions"
harness = false

View File

@@ -59,6 +59,9 @@ compiler_features := --features "$(compiler_features_spaced)"
# Building #
############
bench:
cargo bench $(compiler_features)
build-wasmer:
cargo build --release $(compiler_features)
@@ -93,8 +96,21 @@ build-capi-llvm:
# Testing #
###########
test:
cargo test --release $(compiler_features)
test: $(foreach compiler,$(compilers),test-$(compiler)) test-packages
test-singlepass:
cargo test --release $(compiler_features) --features "test-singlepass"
test-cranelift:
cargo test --release $(compiler_features) --features "test-cranelift"
test-llvm:
cargo test --release $(compiler_features) --features "test-llvm"
test-packages:
cargo test -p wasmer --release
cargo test -p wasmer-runtime --release
cargo test -p wasm-common --release
test-capi-singlepass: build-capi-singlepass
cargo test --manifest-path lib/c-api/Cargo.toml --release \
@@ -114,6 +130,15 @@ test-capi: test-capi-singlepass test-capi-cranelift test-capi-llvm test-capi-ems
# Packaging #
#############
package-wapm:
mkdir -p "package/bin"
ifeq ($(OS), Windows_NT)
echo ""
else
echo "#!/bin/bash\nwapm execute \"\$$@\"" > package/bin/wax
chmod +x package/bin/wax
endif
package-wasmer:
mkdir -p "package/bin"
ifeq ($(OS), Windows_NT)
@@ -155,7 +180,7 @@ package-docs: build-docs build-docs-capi
echo '<!-- Build $(SOURCE_VERSION) --><meta http-equiv="refresh" content="0; url=rust/wasmer_runtime/index.html">' > package/docs/index.html
echo '<!-- Build $(SOURCE_VERSION) --><meta http-equiv="refresh" content="0; url=wasmer_runtime/index.html">' > package/docs/crates/index.html
package: package-wasmer package-capi
package: package-wapm package-wasmer package-capi
cp LICENSE package/LICENSE
cp ATTRIBUTIONS.md package/ATTRIBUTIONS
mkdir -p dist

View File

@@ -8,12 +8,12 @@
<a href="https://dev.azure.com/wasmerio/wasmer/_build/latest?definitionId=3&branchName=master">
<img src="https://img.shields.io/azure-devops/build/wasmerio/wasmer/3.svg?style=flat-square" alt="Build Status">
</a>
<a href="https://slack.wasmer.io">
<img src="https://img.shields.io/static/v1?label=Slack&message=join%20chat&color=brighgreen&style=flat-square" alt="Slack channel">
</a>
<a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/wasmerio/wasmer.svg?style=flat-square" alt="License">
</a>
<a href="https://slack.wasmer.io">
<img src="https://img.shields.io/static/v1?label=Slack&message=join%20chat&color=brighgreen&style=flat-square" alt="Slack channel">
</a>
</p>
<h3>
@@ -21,18 +21,14 @@
<span></span>
<a href="https://docs.wasmer.io">Docs</a>
<span></span>
<a href="https://medium.com/wasmer/">Blog</a>
<span></span>
<a href="https://slack.wasmer.io/">Slack</a>
<span></span>
<a href="https://twitter.com/wasmerio">Twitter</a>
<a href="https://slack.wasmer.io/">Chat</a>
</h3>
</div>
<br />
[Wasmer](https://wasmer.io/) is a standalone [WebAssembly](https://webassembly.org/) runtime:
[Wasmer](https://wasmer.io/) is the fastest and most popular [WebAssembly](https://webassembly.org/) runtime:
* **Universal**: Wasmer is available in *Linux, macOS and Windows* (for both Desktop and [ARM](https://medium.com/wasmer/running-webassembly-on-arm-7d365ed0e50c))
* **Fast**: Wasmer aims to run WebAssembly at near-native speed
* **Pluggable**: Wasmer can be used from almost **any programming language**

View File

@@ -0,0 +1,193 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use test_utils::get_compiler_config_from_str;
use wasmer::*;
use wasmer_engine_jit::JITEngine;
static BASIC_WAT: &str = r#"(module
(func $multiply (import "env" "multiply") (param i32 i32) (result i32))
(func (export "add") (param i32 i32) (result i32)
(i32.add (local.get 0)
(local.get 1)))
(func (export "add20") (param i32 i32 i32 i32 i32
i32 i32 i32 i32 i32
i32 i32 i32 i32 i32
i32 i32 i32 i32 i32) (result i32)
(i32.add
(i32.add
(i32.add (i32.add (i32.add (local.get 0) (local.get 1))
(i32.add (local.get 2) (local.get 3)))
(i32.add (i32.add (local.get 4) (local.get 5))
(i32.add (local.get 6) (local.get 7))))
(i32.add
(i32.add (i32.add (local.get 8) (local.get 9))
(i32.add (local.get 10) (local.get 11)))
(i32.add (i32.add (local.get 12) (local.get 13))
(i32.add (local.get 14) (local.get 15)))))
(i32.add (i32.add (local.get 16) (local.get 17))
(i32.add (local.get 18) (local.get 19))))
)
(func (export "double_then_add") (param i32 i32) (result i32)
(i32.add (call $multiply (local.get 0) (i32.const 2))
(call $multiply (local.get 1) (i32.const 2))))
)"#;
pub fn run_basic_static_function(store: &Store, compiler_name: &str, c: &mut Criterion) {
let module = Module::new(&store, BASIC_WAT).unwrap();
let import_object = imports! {
"env" => {
"multiply" => Function::new(&store, |a: i32, b: i32| a * b),
},
};
let instance = Instance::new(&module, &import_object).unwrap();
let dyn_f: &Function = instance.exports.get("add").unwrap();
let f: NativeFunc<(i32, i32), i32> = dyn_f.native().unwrap();
c.bench_function(&format!("basic static func {}", compiler_name), |b| {
b.iter(|| {
let result = black_box(f.call(4, 6).unwrap());
assert_eq!(result, 10);
})
});
let dyn_f_many: &Function = instance.exports.get("add20").unwrap();
let f_many: NativeFunc<
(
i32,
i32,
i32,
i32,
i32,
i32,
i32,
i32,
i32,
i32,
i32,
i32,
i32,
i32,
i32,
i32,
i32,
i32,
i32,
i32,
),
i32,
> = dyn_f_many.native().unwrap();
c.bench_function(
&format!("basic static func with many args {}", compiler_name),
|b| {
b.iter(|| {
let result = black_box(
f_many
.call(
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
)
.unwrap(),
);
assert_eq!(result, 210);
})
},
);
}
pub fn run_basic_dynamic_function(store: &Store, compiler_name: &str, c: &mut Criterion) {
let module = Module::new(&store, BASIC_WAT).unwrap();
let import_object = imports! {
"env" => {
"multiply" => Function::new(&store, |a: i32, b: i32| a * b),
},
};
let instance = Instance::new(&module, &import_object).unwrap();
let dyn_f: &Function = instance.exports.get("add").unwrap();
c.bench_function(&format!("basic dynfunc {}", compiler_name), |b| {
b.iter(|| {
let dyn_result = black_box(dyn_f.call(&[Val::I32(4), Val::I32(6)]).unwrap());
assert_eq!(dyn_result[0], Val::I32(10));
})
});
let dyn_f_many: &Function = instance.exports.get("add20").unwrap();
c.bench_function(
&format!("basic dynfunc with many args {}", compiler_name),
|b| {
b.iter(|| {
let dyn_result = black_box(
dyn_f_many
.call(&[
Val::I32(1),
Val::I32(2),
Val::I32(3),
Val::I32(4),
Val::I32(5),
Val::I32(6),
Val::I32(7),
Val::I32(8),
Val::I32(9),
Val::I32(10),
Val::I32(11),
Val::I32(12),
Val::I32(13),
Val::I32(14),
Val::I32(15),
Val::I32(16),
Val::I32(17),
Val::I32(18),
Val::I32(19),
Val::I32(20),
])
.unwrap(),
);
assert_eq!(dyn_result[0], Val::I32(210));
})
},
);
}
fn run_static_benchmarks(c: &mut Criterion) {
#[cfg(feature = "llvm")]
{
let store = test_utils::get_default_llvm_store();
run_basic_static_function(&store, "llvm", c);
}
#[cfg(feature = "cranelift")]
{
let store = test_utils::get_default_cranelift_store();
run_basic_static_function(&store, "cranelift", c);
}
#[cfg(feature = "singlepass")]
{
let store = test_utils::get_default_singlepass_store();
run_basic_static_function(&store, "singlepass", c);
}
}
fn run_dynamic_benchmarks(c: &mut Criterion) {
#[cfg(feature = "llvm")]
{
let store = test_utils::get_default_llvm_store();
run_basic_dynamic_function(&store, "llvm", c);
}
#[cfg(feature = "cranelift")]
{
let store = test_utils::get_default_cranelift_store();
run_basic_dynamic_function(&store, "cranelift", c);
}
#[cfg(feature = "singlepass")]
{
let store = test_utils::get_default_singlepass_store();
run_basic_dynamic_function(&store, "singlepass", c);
}
}
criterion_group!(benches, run_static_benchmarks, run_dynamic_benchmarks);
criterion_main!(benches);

View File

@@ -1,6 +1,6 @@
[package]
name = "wasmer"
version = "0.16.2"
version = "1.0.0-alpha.1"
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
description = "Wasmer runtime API"
license = "(Apache-2.0 WITH LLVM-exception) OR MIT"
@@ -9,15 +9,15 @@ readme = "README.md"
edition = "2018"
[dependencies]
wasmer-runtime = { path = "../runtime", version = "0.16.2" }
wasmer-compiler-singlepass = { path = "../compiler-singlepass", version = "0.16.2", optional = true }
wasmer-compiler-cranelift = { path = "../compiler-cranelift", version = "0.16.2", optional = true }
wasmer-compiler-llvm = { path = "../compiler-llvm", version = "0.16.2", optional = true }
wasmer-compiler = { path = "../compiler", version = "0.16.2" }
wasmer-engine = { path = "../engine", version = "0.16.2" }
wasmer-engine-jit = { path = "../engine-jit", version = "0.16.2", optional = true }
wasmer-engine-native = { path = "../engine-native", version = "0.16.2", optional = true }
wasm-common = { path = "../wasm-common", version = "0.16.2" }
wasmer-runtime = { path = "../runtime", version = "1.0.0-alpha.1" }
wasmer-compiler-singlepass = { path = "../compiler-singlepass", version = "1.0.0-alpha.1", optional = true }
wasmer-compiler-cranelift = { path = "../compiler-cranelift", version = "1.0.0-alpha.1", optional = true }
wasmer-compiler-llvm = { path = "../compiler-llvm", version = "1.0.0-alpha.1", optional = true }
wasmer-compiler = { path = "../compiler", version = "1.0.0-alpha.1" }
wasmer-engine = { path = "../engine", version = "1.0.0-alpha.1" }
wasmer-engine-jit = { path = "../engine-jit", version = "1.0.0-alpha.1", optional = true }
wasmer-engine-native = { path = "../engine-native", version = "1.0.0-alpha.1", optional = true }
wasm-common = { path = "../wasm-common", version = "1.0.0-alpha.1" }
indexmap = { version = "1.3", features = ["serde-1"] }
cfg-if = "0.1"
wat = { version = "1.0", optional = true }
@@ -30,7 +30,7 @@ winapi = "0.3"
[dev-dependencies]
# for the binary wasmer.rs
wasmer-engine-dummy = { path = "../../tests/lib/engine-dummy", version = "0.16.2" }
wasmer-engine-dummy = { path = "../../tests/lib/engine-dummy", version = "1.0.0-alpha.1" }
libc = { version = "0.2", default-features = false }
wat = "1.0"
tempfile = "3.1"

View File

@@ -12,11 +12,11 @@ Add to your `Cargo.toml`
```
[dependencies]
wasmer = "0.16.2"
wasmer = "1.0.0-alpha.1"
```
```rust
use wasmer::{Instance, Function, Value, imports, DefaultStore as _};
use wasmer::{Store, Module, Instance, Value, imports};
fn main() -> anyhow::Result<()> {
let module_wat = r#"
@@ -28,10 +28,11 @@ fn main() -> anyhow::Result<()> {
i32.add))
"#;
let module = Module::new(&module_wat);
let store = Store::default();
let module = Module::new(&store, &module_wat);
// The module doesn't import anything, so we create an empty import object.
let import_object = imports! {};
let instance = Instance::new(module, &import_object)?;
let instance = Instance::new(&module, &import_object)?;
let add_one = instance.exports.get_function("add_one")?;
let result = add_one.call([Value::I32(42)])?;
@@ -58,11 +59,15 @@ Wasmer has the following configuration flags:
*This feature is normally used only in development environments*
* Compilers (mutually exclusive):
- `singlepass`: it will use `wasmer-compiler-singlepass` as the default
compiler.
compiler (ideal for **blockchains**).
- `cranelift`: it will use `wasmer-compiler-cranelift` as the default
compiler.
compiler (ideal for **development**).
- `llvm`: it will use `wasmer-compiler-llvm` as the default
compiler.
compiler (ideal for **production**).
Wasmer ships by default with the `cranelift` compiler as its great for development proposes.
However, we strongly encourage to use the `llvm` backend in production as it performs
about 50% faster, achieving near-native speeds.
> Note: if you want to use multiple compilers at the same time, it's also possible!
> You will need to import them directly via each of the compiler crates.

View File

@@ -1,11 +1,13 @@
use crate::externals::{Extern, Function, Global, Memory, Table};
use crate::import_object::LikeNamespace;
use crate::native::NativeFunc;
use indexmap::IndexMap;
use std::{
iter::{ExactSizeIterator, FromIterator},
sync::Arc,
};
use thiserror::Error;
use wasm_common::WasmTypeList;
use wasmer_runtime::Export;
/// The `ExportError` can happen when trying to get a specific
@@ -37,7 +39,7 @@ pub enum ExportError {
/// Exports is a special kind of map that allows easily unwrapping
/// the types of instances.
#[derive(Clone)]
#[derive(Clone, Default)]
pub struct Exports {
map: Arc<IndexMap<String, Extern>>,
}
@@ -45,9 +47,7 @@ pub struct Exports {
impl Exports {
/// Creates a new `Exports`.
pub fn new() -> Self {
Exports {
map: Arc::new(IndexMap::new()),
}
Default::default()
}
/// Creates a new `Exports` with capacity `n`.
@@ -89,7 +89,7 @@ impl Exports {
///
/// If you want to get an export dynamically handling manually
/// type checking manually, please use `get_extern`.
pub fn get<'a, T: Exportable<'a>>(&'a self, name: &str) -> Result<&T, ExportError> {
pub fn get<'a, T: Exportable<'a>>(&'a self, name: &str) -> Result<&'a T, ExportError> {
match self.map.get(name) {
None => Err(ExportError::Missing(name.to_string())),
Some(extern_) => T::get_self_from_extern(extern_),
@@ -116,6 +116,20 @@ impl Exports {
self.get(name)
}
/// Get an export as a `NativeFunc`.
pub fn get_native_function<Args, Rets>(
&self,
name: &str,
) -> Result<NativeFunc<Args, Rets>, ExportError>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
self.get_function(name)?
.native()
.ok_or(ExportError::IncompatibleType)
}
/// Get an export as an `Extern`.
pub fn get_extern(&self, name: &str) -> Option<&Extern> {
self.map.get(name)
@@ -130,9 +144,7 @@ impl Exports {
}
/// Get an iterator over the exports.
pub fn iter<'a>(
&'a self,
) -> ExportsIterator<'a, impl Iterator<Item = (&'a String, &'a Extern)>> {
pub fn iter(&self) -> ExportsIterator<impl Iterator<Item = (&String, &Extern)>> {
ExportsIterator {
iter: self.map.iter(),
}

View File

@@ -3,6 +3,7 @@ use crate::externals::Extern;
use crate::store::Store;
use crate::types::Val;
use crate::FunctionType;
use crate::NativeFunc;
use crate::RuntimeError;
use std::cmp::max;
use wasm_common::{HostFunction, WasmTypeList, WithEnv, WithoutEnv};
@@ -15,7 +16,7 @@ use wasmer_runtime::{
#[derive(Clone, PartialEq)]
pub struct WasmFunctionDefinition {
// The trampoline to do the call
trampoline: VMTrampoline,
pub(crate) trampoline: VMTrampoline,
}
/// The inner helper
@@ -30,11 +31,11 @@ pub enum FunctionDefinition {
/// A WebAssembly `function`.
#[derive(Clone, PartialEq)]
pub struct Function {
store: Store,
definition: FunctionDefinition,
pub(crate) store: Store,
pub(crate) definition: FunctionDefinition,
// If the Function is owned by the Store, not the instance
owned_by_store: bool,
exported: ExportFunction,
pub(crate) owned_by_store: bool,
pub(crate) exported: ExportFunction,
}
impl Function {
@@ -176,18 +177,14 @@ impl Function {
let signature = self.ty();
if signature.params().len() != params.len() {
return Err(RuntimeError::new(format!(
"expected {} arguments, got {}: Parameters of type [{}] did not match signature {}",
signature.params().len(),
params.len(),
"Parameters of type [{}] did not match signature {}",
format_types_for_error_message(params),
&signature
)));
}
if signature.results().len() != results.len() {
return Err(RuntimeError::new(format!(
"expected {} results, got {}: Results of type [{}] did not match signature {}",
signature.results().len(),
results.len(),
"Results of type [{}] did not match signature {}",
format_types_for_error_message(results),
&signature,
)));
@@ -198,7 +195,7 @@ impl Function {
// Store the argument values into `values_vec`.
let param_tys = signature.params().iter();
for ((arg, slot), ty) in params.iter().zip(&mut values_vec).zip(param_tys) {
if arg.ty() != ty.clone() {
if arg.ty() != *ty {
let param_types = format_types_for_error_message(params);
return Err(RuntimeError::new(format!(
"Parameters of type [{}] did not match signature {}",
@@ -252,12 +249,14 @@ impl Function {
/// call the trampoline.
pub fn call(&self, params: &[Val]) -> Result<Box<[Val]>, RuntimeError> {
let mut results = vec![Val::null(); self.result_arity()];
match &self.definition {
FunctionDefinition::Wasm(wasm) => {
self.call_wasm(&wasm, params, &mut results)?;
}
_ => {} // _ => unimplemented!("The host is unimplemented"),
_ => unimplemented!("The function definition isn't supported for the moment"),
}
Ok(results.into_boxed_slice())
}
@@ -286,6 +285,30 @@ impl Function {
vmctx: self.exported.vmctx,
}
}
pub fn native<'a, Args, Rets>(&self) -> Option<NativeFunc<'a, Args, Rets>>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
// type check
if self.exported.signature.params() != Args::wasm_types() {
// todo: error param types don't match
return None;
}
if self.exported.signature.results() != Rets::wasm_types() {
// todo: error result types don't match
return None;
}
Some(NativeFunc::new(
self.store.clone(),
self.exported.address,
self.exported.vmctx,
self.exported.kind,
self.definition.clone(),
))
}
}
impl<'a> Exportable<'a> for Function {
@@ -313,6 +336,7 @@ trait VMDynamicFunction {
}
struct VMDynamicFunctionWithoutEnv {
#[allow(clippy::type_complexity)]
func: Box<dyn Fn(&[Val]) -> Result<Vec<Val>, RuntimeError> + 'static>,
function_type: FunctionType,
}
@@ -330,6 +354,7 @@ struct VMDynamicFunctionWithEnv<Env>
where
Env: Sized,
{
#[allow(clippy::type_complexity)]
func: Box<dyn Fn(&mut Env, &[Val]) -> Result<Vec<Val>, RuntimeError> + 'static>,
env: *mut Env,
function_type: FunctionType,

View File

@@ -45,12 +45,21 @@ impl Memory {
&self.store
}
/// TODO: document this function.
///
/// # Safety
///
/// To be defined (TODO).
pub unsafe fn data_unchecked(&self) -> &[u8] {
self.data_unchecked_mut()
}
/// TODO: document this function, it's trivial to cause UB/break soundness with this
/// method.
///
/// # Safety
///
/// To be defined (TODO).
#[allow(clippy::mut_from_ref)]
pub unsafe fn data_unchecked_mut(&self) -> &mut [u8] {
let definition = self.definition();

View File

@@ -1,4 +1,4 @@
mod function;
pub(crate) mod function;
mod global;
mod memory;
mod table;

View File

@@ -42,9 +42,10 @@ pub trait LikeNamespace {
/// n
/// }
/// ```
#[derive(Clone)]
#[derive(Clone, Default)]
pub struct ImportObject {
map: Arc<Mutex<HashMap<String, Box<dyn LikeNamespace>>>>,
#[allow(clippy::type_complexity)]
pub(crate) state_creator: Option<Arc<dyn Fn() -> (*mut c_void, fn(*mut c_void)) + 'static>>,
/// Allow missing functions to be generated and instantiation to continue when required
/// functions are not provided.
@@ -54,11 +55,7 @@ pub struct ImportObject {
impl ImportObject {
/// Create a new `ImportObject`.
pub fn new() -> Self {
Self {
map: Arc::new(Mutex::new(HashMap::new())),
state_creator: None,
allow_missing_functions: false,
}
Default::default()
}
/// Gets an export given a module and a name
@@ -97,6 +94,7 @@ impl ImportObject {
}
/// Calls the state creator
#[allow(clippy::type_complexity)]
pub fn call_state_creator(&self) -> Option<(*mut c_void, fn(*mut c_void))> {
self.state_creator.as_ref().map(|state_gen| state_gen())
}
@@ -186,8 +184,8 @@ impl IntoIterator for ImportObject {
///
/// [`ImportObject`]: struct.ImportObject.html
///
/// # Usage
///
/// # Usage:
/// ```
/// # use wasmer::{Function, Store};
/// # let store = Store::default();
@@ -196,7 +194,7 @@ impl IntoIterator for ImportObject {
/// let import_object = imports! {
/// "env" => {
/// "foo" => Function::new(&store, foo)
/// }
/// },
/// };
///
/// fn foo(n: i32) -> i32 {
@@ -204,43 +202,45 @@ impl IntoIterator for ImportObject {
/// }
/// ```
#[macro_export]
// TOOD: port of lost fixes of imports macro from wasmer master/imports macro tests
macro_rules! imports {
( $( $ns_name:expr => $ns:tt ),* $(,)? ) => {{
use $crate::ImportObject;
let mut import_object = ImportObject::new();
( $( $ns_name:expr => $ns:tt ),* $(,)? ) => {
{
let mut import_object = $crate::ImportObject::new();
$({
let ns = $crate::import_namespace!($ns);
let namespace = $crate::import_namespace!($ns);
import_object.register($ns_name, ns);
import_object.register($ns_name, namespace);
})*
import_object
}};
}
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! namespace {
($( $imp_name:expr => $import_item:expr ),* $(,)? ) => {
$crate::import_namespace!({ $( $imp_name => $import_item, )* })
($( $import_name:expr => $import_item:expr ),* $(,)? ) => {
$crate::import_namespace!( { $( $import_name => $import_item, )* } )
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! import_namespace {
( { $( $imp_name:expr => $import_item:expr ),* $(,)? } ) => {{
let mut ns = $crate::Exports::new();
( { $( $import_name:expr => $import_item:expr ),* $(,)? } ) => {{
let mut namespace = $crate::Exports::new();
$(
ns.insert($imp_name, $import_item);
namespace.insert($import_name, $import_item);
)*
ns
namespace
}};
($ns:ident) => {
$ns
( $namespace:ident ) => {
$namespace
};
}
@@ -356,4 +356,56 @@ mod test {
false
});
}
#[test]
fn imports_macro_allows_trailing_comma_and_none() {
use crate::Function;
let store = Default::default();
fn func(arg: i32) -> i32 {
arg + 1
}
let _ = imports! {
"env" => {
"func" => Function::new(&store, func),
},
};
let _ = imports! {
"env" => {
"func" => Function::new(&store, func),
}
};
let _ = imports! {
"env" => {
"func" => Function::new(&store, func),
},
"abc" => {
"def" => Function::new(&store, func),
}
};
let _ = imports! {
"env" => {
"func" => Function::new(&store, func)
},
};
let _ = imports! {
"env" => {
"func" => Function::new(&store, func)
}
};
let _ = imports! {
"env" => {
"func1" => Function::new(&store, func),
"func2" => Function::new(&store, func)
}
};
let _ = imports! {
"env" => {
"func1" => Function::new(&store, func),
"func2" => Function::new(&store, func),
}
};
}
}

View File

@@ -7,11 +7,13 @@ mod import_object;
mod instance;
mod memory_view;
mod module;
mod native;
mod ordered_resolver;
mod ptr;
mod store;
mod tunables;
mod types;
mod utils;
pub use crate::exports::{ExportError, Exportable, Exports};
pub use crate::externals::{Extern, Function, Global, Memory, Table};
@@ -19,6 +21,7 @@ pub use crate::import_object::{ImportObject, ImportObjectIterator, LikeNamespace
pub use crate::instance::Instance;
pub use crate::memory_view::{Atomically, MemoryView};
pub use crate::module::Module;
pub use crate::native::NativeFunc;
pub use crate::ordered_resolver::OrderedResolver;
pub use crate::ptr::{Array, Item, WasmPtr};
pub use crate::store::{Store, StoreObject};
@@ -28,6 +31,7 @@ pub use crate::types::{
MemoryType, Mutability, TableType, Val, ValType,
};
pub use crate::types::{Val as Value, ValType as Type};
pub use crate::utils::is_wasm;
pub use target_lexicon::{Architecture, CallingConvention, OperatingSystem, Triple, HOST};
pub use wasm_common::{Bytes, Pages, ValueType, WasmExternType, WasmTypeList};
@@ -39,6 +43,8 @@ pub use wasmer_engine::{
NamedResolverChain, Resolver, RuntimeError, SerializeError,
};
pub use wasmer_runtime::{raise_user_trap, MemoryError};
#[cfg(feature = "wat")]
pub use wat::parse_bytes as wat2wasm;
// The compilers are mutually exclusive
#[cfg(any(

View File

@@ -33,11 +33,6 @@ pub enum IoCompileError {
pub struct Module {
store: Store,
artifact: Arc<dyn Artifact>,
#[cfg(feature = "wat")]
#[doc(hidden)]
// If the module was compiled from a wat file.
pub from_wat: bool,
}
impl Module {
@@ -103,20 +98,12 @@ impl Module {
#[allow(unreachable_code)]
pub fn new(store: &Store, bytes: impl AsRef<[u8]>) -> Result<Module, CompileError> {
#[cfg(feature = "wat")]
{
let might_be_wat = !bytes.as_ref().starts_with(b"\0asm");
let bytes = wat::parse_bytes(bytes.as_ref()).map_err(|e| {
CompileError::Wasm(WasmError::Generic(format!(
"Error when converting wat: {}",
e
)))
})?;
let mut module = Module::from_binary(store, bytes.as_ref())?;
// We can assume it was a wat file if is not "wasm" looking
// and the previous step succeeded.
module.from_wat = might_be_wat;
return Ok(module);
}
Module::from_binary(store, bytes.as_ref())
}
@@ -145,6 +132,8 @@ impl Module {
/// Creates a new WebAssembly module skipping any kind of validation.
///
/// # Safety
///
/// This can speed up compilation time a bit, but it should be only used
/// in environments where the WebAssembly modules are trusted and validated
/// beforehand.
@@ -264,8 +253,6 @@ impl Module {
Module {
store: store.clone(),
artifact,
#[cfg(feature = "wat")]
from_wat: false,
}
}

190
lib/api/src/native.rs Normal file
View File

@@ -0,0 +1,190 @@
// Native Funcs
// use wasmer_runtime::ExportFunction;
use std::marker::PhantomData;
use crate::externals::function::{FunctionDefinition, WasmFunctionDefinition};
use crate::{Function, FunctionType, RuntimeError, Store};
use wasm_common::{NativeWasmType, WasmExternType, WasmTypeList};
use wasmer_runtime::{
wasmer_call_trampoline, ExportFunction, VMContext, VMFunctionBody, VMFunctionKind,
};
#[derive(Clone)]
pub struct UnprovidedArgs;
#[derive(Clone)]
pub struct UnprovidedRets;
pub struct NativeFunc<'a, Args = UnprovidedArgs, Rets = UnprovidedRets> {
definition: FunctionDefinition,
store: Store,
address: *const VMFunctionBody,
vmctx: *mut VMContext,
arg_kind: VMFunctionKind,
// exported: ExportFunction,
_phantom: PhantomData<(&'a (), Args, Rets)>,
}
unsafe impl<'a, Args, Rets> Send for NativeFunc<'a, Args, Rets> {}
impl<'a, Args, Rets> NativeFunc<'a, Args, Rets>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
pub(crate) fn new(
store: Store,
address: *const VMFunctionBody,
vmctx: *mut VMContext,
arg_kind: VMFunctionKind,
definition: FunctionDefinition,
) -> Self {
Self {
definition,
store,
address,
vmctx,
arg_kind,
_phantom: PhantomData,
}
}
}
impl<'a, Args, Rets> From<&NativeFunc<'a, Args, Rets>> for ExportFunction
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
fn from(other: &NativeFunc<'a, Args, Rets>) -> Self {
let signature = FunctionType::new(Args::wasm_types(), Rets::wasm_types());
Self {
address: other.address,
vmctx: other.vmctx,
signature,
kind: other.arg_kind,
}
}
}
impl<'a, Args, Rets> From<NativeFunc<'a, Args, Rets>> for Function
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
fn from(other: NativeFunc<'a, Args, Rets>) -> Self {
let signature = FunctionType::new(Args::wasm_types(), Rets::wasm_types());
Self {
store: other.store,
definition: other.definition,
owned_by_store: true, // todo
exported: ExportFunction {
address: other.address,
vmctx: other.vmctx,
signature,
kind: other.arg_kind,
},
}
}
}
macro_rules! impl_native_traits {
( $( $x:ident ),* ) => {
#[allow(unused_parens, non_snake_case)]
impl<'a $( , $x )*, Rets> NativeFunc<'a, ( $( $x, )* ), Rets>
where
$( $x: WasmExternType, )*
Rets: WasmTypeList,
{
/// Call the typed func and return results.
pub fn call(&self, $( $x: $x, )* ) -> Result<Rets, RuntimeError> {
// TODO: when `const fn` related features mature more, we can declare a single array
// of the correct size here.
let mut params_list = [ $( $x.to_native().to_binary() ),* ];
let mut rets_list_array = Rets::empty_array();
let rets_list = rets_list_array.as_mut();
let using_rets_array;
let args_rets: &mut [i128] = if params_list.len() > rets_list.len() {
using_rets_array = false;
params_list.as_mut()
} else {
using_rets_array = true;
for (i, &arg) in params_list.iter().enumerate() {
rets_list[i] = arg;
}
rets_list.as_mut()
};
match self.definition {
FunctionDefinition::Wasm(WasmFunctionDefinition {
trampoline
}) => {
unsafe {
wasmer_call_trampoline(
self.vmctx,
trampoline,
self.address,
args_rets.as_mut_ptr() as *mut u8,
)
}?;
let num_rets = rets_list.len();
if !using_rets_array && num_rets > 0 {
let src_pointer = params_list.as_ptr();
let rets_list = &mut rets_list_array.as_mut()[0] as *mut i128;
unsafe {
// TODO: we can probably remove this copy by doing some clever `transmute`s.
// we know it's not overlapping because `using_rets_array` is false
std::ptr::copy_nonoverlapping(src_pointer,
rets_list,
num_rets);
}
}
return Ok(Rets::from_array(rets_list_array));
}
FunctionDefinition::Host => {
if self.arg_kind == VMFunctionKind::Static {
unsafe {
let f = std::mem::transmute::<_, unsafe fn( *mut VMContext, $( $x, )*) -> Rets>(self.address);
let results = f( self.vmctx, $( $x, )* );
return Ok(results);
}
} else {
todo!("dynamic host functions not yet implemented")
}
},
}
}
}
};
}
// impl_native_traits!();
impl_native_traits!();
impl_native_traits!(A1);
impl_native_traits!(A1, A2);
impl_native_traits!(A1, A2, A3);
impl_native_traits!(A1, A2, A3, A4);
impl_native_traits!(A1, A2, A3, A4, A5);
impl_native_traits!(A1, A2, A3, A4, A5, A6);
impl_native_traits!(A1, A2, A3, A4, A5, A6, A7);
impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8);
impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9);
impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11);
impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12);
impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13);
impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14);
impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15);
impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16);
impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17);
impl_native_traits!(
A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18
);
impl_native_traits!(
A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19
);
impl_native_traits!(
A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20
);
// impl_native_traits!(A1, A2, A3);

View File

@@ -21,7 +21,7 @@ impl Store {
}
pub fn same(a: &Store, b: &Store) -> bool {
Arc::ptr_eq(&a.engine, &b.engine)
a.engine.id() == b.engine.id()
}
}

View File

@@ -41,15 +41,12 @@ impl Tunables {
// wasting too much memory.
let dynamic_memory_offset_guard_size: u64 = 0x1_0000;
match triple.operating_system {
OperatingSystem::Windows => {
if let OperatingSystem::Windows = triple.operating_system {
// For now, use a smaller footprint on Windows so that we don't
// don't outstrip the paging file.
// outstrip the paging file.
static_memory_bound = min(static_memory_bound, 0x100.into());
static_memory_offset_guard_size = min(static_memory_offset_guard_size, 0x10000);
}
_ => {}
}
Self {
static_memory_bound,
@@ -66,7 +63,7 @@ impl BaseTunables for Tunables {
// tunables make it static.
//
// If the module doesn't declare an explicit maximum treat it as 4GiB.
let maximum = memory.maximum.unwrap_or(Pages::max_value());
let maximum = memory.maximum.unwrap_or_else(Pages::max_value);
if maximum <= self.static_memory_bound {
assert_ge!(self.static_memory_bound, memory.minimum);
MemoryPlan {

4
lib/api/src/utils.rs Normal file
View File

@@ -0,0 +1,4 @@
/// Check if the provided bytes are wasm-like
pub fn is_wasm(bytes: impl AsRef<[u8]>) -> bool {
bytes.as_ref().starts_with(b"\0asm")
}

View File

@@ -117,13 +117,12 @@ fn table_grow() -> Result<()> {
let f = Function::new(&store, |num: i32| num + 1);
let table = Table::new(&store, table_type, Value::FuncRef(f.clone()))?;
// Growing to a bigger maximum should return None
let new_len = table.grow(12, Value::FuncRef(f.clone()));
assert!(new_len.is_err());
let old_len = table.grow(12, Value::FuncRef(f.clone()));
assert!(old_len.is_err());
// Growing to a bigger maximum should return None
let new_len = table.grow(5, Value::FuncRef(f.clone()))?;
// TODO: new len should be instead previous length, similarly to memory
assert_eq!(new_len, 5);
let old_len = table.grow(5, Value::FuncRef(f.clone()))?;
assert_eq!(old_len, 0);
Ok(())
}
@@ -330,3 +329,41 @@ fn function_new_dynamic_env() -> Result<()> {
assert_eq!(function.ty().clone(), function_type);
Ok(())
}
#[test]
fn native_function_works() -> Result<()> {
let store = Store::default();
let function = Function::new(&store, || {});
let native_function: NativeFunc<(), ()> = function.native().unwrap();
let result = native_function.call();
assert!(result.is_ok());
// TODO:
/*let function = Function::new(&store, |a: i32| -> i32 { a + 1 });
let native_function: NativeFunc<i32, i32> = function.native().unwrap();
assert!(native_function.call(3).unwrap(), 4);
*/
fn rust_abi(a: i32, b: i64, c: f32, d: f64) -> u64 {
(a as u64 * 1000) + (b as u64 * 100) + (c as u64 * 10) + (d as u64)
}
let function = Function::new(&store, rust_abi);
let native_function: NativeFunc<(i32, i64, f32, f64), u64> = function.native().unwrap();
assert_eq!(native_function.call(8, 4, 1.5, 5.).unwrap(), 8415);
let function = Function::new(&store, || -> i32 { 1 });
let native_function: NativeFunc<(), i32> = function.native().unwrap();
assert_eq!(native_function.call().unwrap(), 1);
// TODO:
/*let function = Function::new(&store, |_a: i32| {});
let native_function: NativeFunc<i32, ()> = function.native().unwrap();
assert!(native_function.call(4).is_ok());*/
// TODO:
/*let function = Function::new(&store, || -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) });
let native_function: NativeFunc<(), (i32, i64, f32, f64)> = function.native().unwrap();
assert_eq!(native_function.call().unwrap(), (1, 2, 3.0, 4.0));
*/
Ok(())
}

View File

@@ -1,6 +1,6 @@
[package]
name = "wasmer-c-api"
version = "0.16.2"
version = "1.0.0-alpha.1"
description = "Wasmer C API library"
documentation = "https://wasmerio.github.io/wasmer/c-api/"
license = "MIT"
@@ -23,28 +23,28 @@ thiserror = "1"
paste = "0.1"
# for generating code in the same way thot the wasm-c-api does
# Commented out for now until we can find a solution to the exported function problem
# wasmer-wasm-c-api = { version = "0.16.2", path = "crates/wasm-c-api" }
# wasmer-wasm-c-api = { version = "1.0.0-alpha.1", path = "crates/wasm-c-api" }
[dependencies.wasmer]
default-features = false
features = ["compiler", "engine", "jit"]
path = "../api"
version = "0.16.2"
version = "1.0.0-alpha.1"
[dependencies.wasm-common]
default-features = false
path = "../wasm-common"
version = "0.16.2"
version = "1.0.0-alpha.1"
[dependencies.wasmer-wasi]
default-features = false
path = "../wasi"
version = "0.16.2"
version = "1.0.0-alpha.1"
optional = true
#[dependencies.wasmer-emscripten]
#path = "../emscripten"
#version = "0.16.2"
#version = "1.0.0-alpha.1"
#optional = true
[features]

View File

@@ -50,7 +50,7 @@ int main()
int error_result = wasmer_last_error_message(error_str, error_len);
assert(error_len == error_result);
printf("Error str: `%s`\n", error_str);
assert(0 == strcmp(error_str, "RuntimeError: expected 2 arguments, got 1: Parameters of type [I32] did not match signature [I32, I32] -> [I32]"));
assert(0 == strcmp(error_str, "RuntimeError: Parameters of type [I32] did not match signature [I32, I32] -> [I32]"));
free(error_str);
printf("Destroy instance\n");

View File

@@ -1,6 +1,6 @@
[package]
name = "wasmer-cache"
version = "0.16.2"
version = "1.0.0-alpha.1"
description = "Cache system for WebAssembly"
license = "MIT"
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
@@ -11,7 +11,7 @@ keywords = ["webassembly", "wasm", "cache"]
edition = "2018"
[dependencies]
wasmer = { path = "../api", version = "0.16.2", default-features = false }
wasmer = { path = "../api", version = "1.0.0-alpha.1", default-features = false }
memmap = "0.7"
hex = "0.4"
thiserror = "1"

View File

@@ -20,5 +20,5 @@ pub trait Cache {
unsafe fn load(&self, store: &Store, key: WasmHash) -> Result<Module, Self::DeserializeError>;
/// Store a [`Module`] into the cache with the given [`WasmHash`].
fn store(&mut self, key: WasmHash, module: Module) -> Result<(), Self::SerializeError>;
fn store(&mut self, key: WasmHash, module: &Module) -> Result<(), Self::SerializeError>;
}

View File

@@ -78,7 +78,7 @@ impl Cache for FileSystemCache {
Module::deserialize_from_file(&store, new_path_buf)
}
fn store(&mut self, key: WasmHash, module: Module) -> Result<(), Self::SerializeError> {
fn store(&mut self, key: WasmHash, module: &Module) -> Result<(), Self::SerializeError> {
let filename = key.to_string();
let mut new_path_buf = self.path.clone();

View File

@@ -1,6 +1,6 @@
[package]
name = "wasmer-compiler-cranelift"
version = "0.16.2"
version = "1.0.0-alpha.1"
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
description = "Standalone environment support for WebAsssembly code in Cranelift"
license = "MIT OR (Apache-2.0 WITH LLVM-exception)"
@@ -12,9 +12,9 @@ readme = "README.md"
edition = "2018"
[dependencies]
wasmer-compiler = { path = "../compiler", version = "0.16.2", features = ["translator"], default-features = false }
wasmer-runtime = { path = "../runtime", version = "0.16.2" }
wasm-common = { path = "../wasm-common", version = "0.16.2", default-features = false }
wasmer-compiler = { path = "../compiler", version = "1.0.0-alpha.1", features = ["translator"], default-features = false }
wasmer-runtime = { path = "../runtime", version = "1.0.0-alpha.1" }
wasm-common = { path = "../wasm-common", version = "1.0.0-alpha.1", default-features = false }
cranelift-codegen = { version = "0.62", default-features = false }
cranelift-frontend = { version = "0.62", default-features = false }
tracing = "0.1"

View File

@@ -81,7 +81,7 @@ impl Compiler for CraneliftCompiler {
let signatures = module
.signatures
.iter()
.map(|(_sig_index, func_type)| signature_to_cranelift_ir(func_type, &frontend_config))
.map(|(_sig_index, func_type)| signature_to_cranelift_ir(func_type, frontend_config))
.collect::<PrimaryMap<SignatureIndex, ir::Signature>>();
let functions = function_body_inputs

View File

@@ -715,7 +715,7 @@ impl<'module_environment> BaseFuncEnvironment for FuncEnvironment<'module_enviro
Ok(GlobalVariable::Memory {
gv: ptr,
offset: offset.into(),
ty: type_to_irtype(self.module.globals[index].ty, &self.target_config())?,
ty: type_to_irtype(self.module.globals[index].ty, self.target_config())?,
})
}

View File

@@ -26,7 +26,7 @@ pub fn make_trampoline_dynamic_function(
) -> Result<FunctionBody, CompileError> {
let pointer_type = isa.pointer_type();
let frontend_config = isa.frontend_config();
let signature = signature_to_cranelift_ir(func_type, &frontend_config);
let signature = signature_to_cranelift_ir(func_type, frontend_config);
let mut stub_sig = ir::Signature::new(frontend_config.default_call_conv);
// Add the caller `vmctx` parameter.
stub_sig.params.push(ir::AbiParam::special(

View File

@@ -27,7 +27,7 @@ pub fn make_trampoline_function_call(
) -> Result<FunctionBody, CompileError> {
let pointer_type = isa.pointer_type();
let frontend_config = isa.frontend_config();
let signature = signature_to_cranelift_ir(func_type, &frontend_config);
let signature = signature_to_cranelift_ir(func_type, frontend_config);
let mut wrapper_sig = ir::Signature::new(frontend_config.default_call_conv);
// Add the callee `vmctx` parameter.

View File

@@ -66,7 +66,7 @@ pub trait TargetEnvironment {
///
/// This returns `R64` for 64-bit architectures and `R32` for 32-bit architectures.
fn reference_type(&self) -> ir::Type {
reference_type(&self.target_config()).expect("expected reference type")
reference_type(self.target_config()).expect("expected reference type")
}
}

View File

@@ -18,7 +18,7 @@ use wasmer_runtime::libcalls::LibCall;
/// Helper function translate a Function signature into Cranelift Ir
pub fn signature_to_cranelift_ir(
signature: &FunctionType,
target_config: &TargetFrontendConfig,
target_config: TargetFrontendConfig,
) -> ir::Signature {
let mut sig = ir::Signature::new(target_config.default_call_conv);
sig.params.extend(signature.params().iter().map(|&ty| {
@@ -40,7 +40,7 @@ pub fn signature_to_cranelift_ir(
}
/// Helper function translating wasmparser types to Cranelift types when possible.
pub fn reference_type(target_config: &TargetFrontendConfig) -> WasmResult<ir::Type> {
pub fn reference_type(target_config: TargetFrontendConfig) -> WasmResult<ir::Type> {
match target_config.pointer_type() {
ir::types::I32 => Ok(ir::types::R32),
ir::types::I64 => Ok(ir::types::R64),
@@ -51,7 +51,7 @@ pub fn reference_type(target_config: &TargetFrontendConfig) -> WasmResult<ir::Ty
}
/// Helper function translating wasmparser types to Cranelift types when possible.
pub fn type_to_irtype(ty: Type, target_config: &TargetFrontendConfig) -> WasmResult<ir::Type> {
pub fn type_to_irtype(ty: Type, target_config: TargetFrontendConfig) -> WasmResult<ir::Type> {
match ty {
Type::I32 => Ok(ir::types::I32),
Type::I64 => Ok(ir::types::I64),

View File

@@ -1,6 +1,6 @@
[package]
name = "wasmer-compiler-llvm"
version = "0.16.2"
version = "1.0.0-alpha.1"
license = "MIT"
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
repository = "https://github.com/wasmerio/wasmer"
@@ -10,9 +10,9 @@ edition = "2018"
readme = "README.md"
[dependencies]
wasmer-compiler = { path = "../compiler", version = "0.16.2", features = ["translator"] }
wasmer-runtime = { path = "../runtime", version = "0.16.2" }
wasm-common = { path = "../wasm-common", version = "0.16.2" }
wasmer-compiler = { path = "../compiler", version = "1.0.0-alpha.1", features = ["translator"] }
wasmer-runtime = { path = "../runtime", version = "1.0.0-alpha.1" }
wasm-common = { path = "../wasm-common", version = "1.0.0-alpha.1" }
target-lexicon = { version = "0.10", default-features = false }
smallvec = "1"
goblin = "0.2"

View File

@@ -69,26 +69,6 @@ impl LLVMConfig {
/// Creates a new configuration object with the default configuration
/// specified.
pub fn new(features: Features, target: Target) -> Self {
let operating_system =
if target.triple().operating_system == wasmer_compiler::OperatingSystem::Darwin {
// LLVM detects static relocation + darwin + 64-bit and
// force-enables PIC because MachO doesn't support that
// combination. They don't check whether they're targeting
// MachO, they check whether the OS is set to Darwin.
//
// Since both linux and darwin use SysV ABI, this should work.
wasmer_compiler::OperatingSystem::Linux
} else {
target.triple().operating_system
};
let triple = Triple {
architecture: target.triple().architecture,
vendor: target.triple().vendor.clone(),
operating_system,
environment: target.triple().environment,
binary_format: target_lexicon::BinaryFormat::Elf,
};
let target = Target::new(triple, *target.cpu_features());
Self {
enable_nan_canonicalization: true,
enable_verifier: false,
@@ -112,7 +92,27 @@ impl LLVMConfig {
}
pub fn target_triple(&self) -> TargetTriple {
TargetTriple::create(&self.target().triple().to_string())
let target = self.target();
let operating_system =
if target.triple().operating_system == wasmer_compiler::OperatingSystem::Darwin {
// LLVM detects static relocation + darwin + 64-bit and
// force-enables PIC because MachO doesn't support that
// combination. They don't check whether they're targeting
// MachO, they check whether the OS is set to Darwin.
//
// Since both linux and darwin use SysV ABI, this should work.
wasmer_compiler::OperatingSystem::Linux
} else {
target.triple().operating_system
};
let triple = Triple {
architecture: target.triple().architecture,
vendor: target.triple().vendor.clone(),
operating_system,
environment: target.triple().environment,
binary_format: target_lexicon::BinaryFormat::Elf,
};
TargetTriple::create(&triple.to_string())
}
/// Generates the target machine for the current target
@@ -149,10 +149,11 @@ impl LLVMConfig {
.map(|feature| format!("+{}", feature.to_string()))
.join(",");
let llvm_target = InkwellTarget::from_triple(&self.target_triple()).unwrap();
let target_triple = self.target_triple();
let llvm_target = InkwellTarget::from_triple(&target_triple).unwrap();
llvm_target
.create_target_machine(
&self.target_triple(),
&target_triple,
"generic",
&llvm_cpu_features,
self.opt_level,

View File

@@ -1,6 +1,9 @@
use crate::config::{CompiledFunctionKind, LLVMConfig};
use crate::object_file::load_object_file;
use crate::translator::abi::{func_type_to_llvm, is_sret, rets_from_call};
use crate::translator::abi::{
func_type_to_llvm, get_vmctx_ptr_param, is_sret, pack_values_for_register_return,
rets_from_call,
};
use crate::translator::intrinsics::{type_to_llvm, type_to_llvm_ptr, Intrinsics};
use inkwell::{
attributes::{Attribute, AttributeLoc},
@@ -14,7 +17,6 @@ use inkwell::{
};
use std::cmp;
use std::convert::TryInto;
use std::iter;
use wasm_common::{FunctionType, Type};
use wasmer_compiler::{CompileError, FunctionBody};
@@ -48,7 +50,7 @@ impl FuncTrampoline {
let (callee_ty, callee_attrs) = func_type_to_llvm(&self.ctx, &intrinsics, ty)?;
let trampoline_ty = intrinsics.void_ty.fn_type(
&[
intrinsics.ctx_ptr_ty.as_basic_type_enum(), // callee_vmctx ptr
intrinsics.ctx_ptr_ty.as_basic_type_enum(), // vmctx ptr
callee_ty
.ptr_type(AddressSpace::Generic)
.as_basic_type_enum(), // callee function address
@@ -134,16 +136,11 @@ impl FuncTrampoline {
module.set_data_layout(&target_machine.get_target_data().get_data_layout());
let intrinsics = Intrinsics::declare(&module, &self.ctx);
let params = iter::once(Ok(intrinsics.ctx_ptr_ty.as_basic_type_enum()))
.chain(
ty.params()
.iter()
.map(|param_ty| type_to_llvm(&intrinsics, *param_ty)),
)
.collect::<Result<Vec<_>, _>>()?;
let trampoline_ty = intrinsics.void_ty.fn_type(params.as_slice(), false);
let (trampoline_ty, trampoline_attrs) = func_type_to_llvm(&self.ctx, &intrinsics, ty)?;
let trampoline_func = module.add_function("", trampoline_ty, Some(Linkage::External));
for (attr, attr_loc) in trampoline_attrs {
trampoline_func.add_attribute(attr_loc, attr);
}
trampoline_func
.as_global_value()
.set_section(FUNCTION_SECTION);
@@ -311,14 +308,6 @@ fn generate_dynamic_trampoline<'ctx>(
let builder = context.create_builder();
builder.position_at_end(entry_block);
/*
// TODO: remove debugging
builder.build_call(
intrinsics.debug_trap,
&[],
"");
*/
// Allocate stack space for the params and results.
let values = builder.build_alloca(
intrinsics.i128_ty.array_type(cmp::max(
@@ -329,6 +318,7 @@ fn generate_dynamic_trampoline<'ctx>(
);
// Copy params to 'values'.
let first_user_param = if is_sret(func_sig)? { 2 } else { 1 };
for i in 0..func_sig.params().len() {
let ptr = unsafe {
builder.build_in_bounds_gep(
@@ -343,57 +333,99 @@ fn generate_dynamic_trampoline<'ctx>(
let ptr = builder
.build_bitcast(ptr, type_to_llvm_ptr(intrinsics, func_sig.params()[i])?, "")
.into_pointer_value();
builder.build_store(ptr, trampoline_func.get_nth_param(i as u32 + 1).unwrap());
builder.build_store(
ptr,
trampoline_func
.get_nth_param(i as u32 + first_user_param)
.unwrap(),
);
}
let callee_ty = intrinsics
.void_ty
.fn_type(
&[
intrinsics.ctx_ptr_ty.as_basic_type_enum(),
intrinsics.i128_ptr_ty.as_basic_type_enum(),
intrinsics.ctx_ptr_ty.as_basic_type_enum(), // vmctx ptr
intrinsics.i128_ptr_ty.as_basic_type_enum(), // in/out values ptr
],
false,
)
.ptr_type(AddressSpace::Generic)
.ptr_type(AddressSpace::Generic);
let vmctx = trampoline_func.get_nth_param(0).unwrap();
let vmctx = get_vmctx_ptr_param(&trampoline_func);
let callee = builder
.build_load(
builder
.build_bitcast(vmctx, callee_ty, "")
.build_bitcast(vmctx, callee_ty.ptr_type(AddressSpace::Generic), "")
.into_pointer_value(),
"",
)
.into_pointer_value();
let values_ptr = builder.build_pointer_cast(values, intrinsics.i128_ptr_ty, "");
builder.build_call(
callee,
&[vmctx.as_basic_value_enum(), values.as_basic_value_enum()],
&[
vmctx.as_basic_value_enum(),
values_ptr.as_basic_value_enum(),
],
"",
);
match func_sig.results() {
[] => {
if func_sig.results().is_empty() {
builder.build_return(None);
}
[ty] => {
} else {
let results = func_sig
.results()
.iter()
.enumerate()
.map(|(idx, ty)| {
let ptr = unsafe {
builder.build_in_bounds_gep(
builder.build_gep(
values,
&[intrinsics.i32_zero, intrinsics.i32_ty.const_int(0, false)],
&[
intrinsics.i32_ty.const_zero(),
intrinsics.i32_ty.const_int(idx.try_into().unwrap(), false),
],
"",
)
};
let ptr = builder
.build_bitcast(ptr, type_to_llvm_ptr(intrinsics, *ty)?, "")
let ptr = builder.build_pointer_cast(ptr, type_to_llvm_ptr(intrinsics, *ty)?, "");
Ok(builder.build_load(ptr, ""))
})
.collect::<Result<Vec<_>, CompileError>>()?;
if is_sret(func_sig)? {
let sret = trampoline_func
.get_first_param()
.unwrap()
.into_pointer_value();
let ret = builder.build_load(ptr, "");
builder.build_return(Some(&ret));
let mut struct_value = sret
.get_type()
.get_element_type()
.into_struct_type()
.get_undef();
for (idx, value) in results.iter().enumerate() {
let value = builder.build_bitcast(
*value,
type_to_llvm(&intrinsics, func_sig.results()[idx])?,
"",
);
struct_value = builder
.build_insert_value(struct_value, value, idx as u32, "")
.unwrap()
.into_struct_value();
}
builder.build_store(sret, struct_value);
builder.build_return(None);
} else {
builder.build_return(Some(&pack_values_for_register_return(
&intrinsics,
&builder,
&results.as_slice(),
&trampoline_func.get_type(),
)?));
}
}
_ => unimplemented!("multi-value return is not yet implemented"),
};
Ok(())
}

View File

@@ -110,10 +110,12 @@ impl FuncTranslator {
let entry = self.ctx.append_basic_block(func, "entry");
let start_of_code = self.ctx.append_basic_block(func, "start_of_code");
let return_ = self.ctx.append_basic_block(func, "return");
let alloca_builder = self.ctx.create_builder();
let cache_builder = self.ctx.create_builder();
let builder = self.ctx.create_builder();
cache_builder.position_at_end(entry);
let br = cache_builder.build_unconditional_branch(start_of_code);
alloca_builder.position_before(&br);
cache_builder.position_before(&br);
builder.position_at_end(start_of_code);
@@ -139,14 +141,23 @@ impl FuncTranslator {
} else {
1
};
let mut is_first_alloca = true;
let mut insert_alloca = |ty, name| {
let alloca = alloca_builder.build_alloca(ty, name);
if is_first_alloca {
alloca_builder.position_at(entry, &alloca.as_instruction_value().unwrap());
is_first_alloca = false;
}
alloca
};
for idx in 0..wasm_fn_type.params().len() {
let ty = wasm_fn_type.params()[idx];
let ty = type_to_llvm(&intrinsics, ty)?;
let value = func
.get_nth_param((idx as u32).checked_add(first_param).unwrap())
.unwrap();
// TODO: don't interleave allocas and stores.
let alloca = cache_builder.build_alloca(ty, "param");
let alloca = insert_alloca(ty, "param");
cache_builder.build_store(alloca, value);
params.push(alloca);
}
@@ -160,9 +171,8 @@ impl FuncTranslator {
.map_err(to_wasm_error)?;
let ty = wptype_to_type(ty).map_err(to_compile_error)?;
let ty = type_to_llvm(&intrinsics, ty)?;
// TODO: don't interleave allocas and stores.
for _ in 0..count {
let alloca = cache_builder.build_alloca(ty, "local");
let alloca = insert_alloca(ty, "local");
cache_builder.build_store(alloca, const_zero(ty));
locals.push(alloca);
}
@@ -174,6 +184,7 @@ impl FuncTranslator {
let mut fcg = LLVMFunctionCodeGenerator {
context: &self.ctx,
builder,
alloca_builder,
intrinsics: &intrinsics,
state,
function: func,
@@ -608,7 +619,7 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
let divisor_is_zero = self.builder.build_int_compare(
IntPredicate::EQ,
right,
int_type.const_int(0, false),
int_type.const_zero(),
"divisor_is_zero",
);
let should_trap = self.builder.build_or(
@@ -633,10 +644,7 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
self.intrinsics.expect_i1,
&[
should_trap.as_basic_value_enum(),
self.intrinsics
.i1_ty
.const_int(0, false)
.as_basic_value_enum(),
self.intrinsics.i1_ty.const_zero().as_basic_value_enum(),
],
"should_trap_expect",
)
@@ -671,7 +679,7 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
let should_trap = self.builder.build_int_compare(
IntPredicate::EQ,
value,
int_type.const_int(0, false),
int_type.const_zero(),
"divisor_is_zero",
);
@@ -681,10 +689,7 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
self.intrinsics.expect_i1,
&[
should_trap.as_basic_value_enum(),
self.intrinsics
.i1_ty
.const_int(0, false)
.as_basic_value_enum(),
self.intrinsics.i1_ty.const_zero().as_basic_value_enum(),
],
"should_trap_expect",
)
@@ -1179,7 +1184,7 @@ fn emit_stack_map<'ctx>(
.const_int(stackmap_id as u64, false)
.as_basic_value_enum(),
);
params.push(intrinsics.i32_ty.const_int(0, false).as_basic_value_enum());
params.push(intrinsics.i32_ty.const_zero().as_basic_value_enum());
let locals: Vec<_> = locals.iter().map(|x| x.as_basic_value_enum()).collect();
let mut value_semantics: Vec<ValueSemantic> =
@@ -1226,7 +1231,7 @@ fn finalize_opcode_stack_map<'ctx>(
.i64_ty
.const_int(stackmap_id as u64, false)
.as_basic_value_enum(),
intrinsics.i32_ty.const_int(0, false).as_basic_value_enum(),
intrinsics.i32_ty.const_zero().as_basic_value_enum(),
],
"opcode_stack_map_end",
);
@@ -1245,6 +1250,7 @@ fn finalize_opcode_stack_map<'ctx>(
pub struct LLVMFunctionCodeGenerator<'ctx, 'a> {
context: &'ctx Context,
builder: Builder<'ctx>,
alloca_builder: Builder<'ctx>,
intrinsics: &'a Intrinsics<'ctx>,
state: State<'ctx>,
function: FunctionValue<'ctx>,
@@ -1390,7 +1396,7 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
);
let signal_mem = ctx.signal_mem();
let iv = self.builder
.build_store(signal_mem, self.context.i8_type().const_int(0 as u64, false));
.build_store(signal_mem, self.context.i8_type().const_zero());
// Any 'store' can be made volatile.
iv.set_volatile(true).unwrap();
finalize_opcode_stack_map(
@@ -1617,14 +1623,13 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
let current_block = self.builder.get_insert_block().ok_or_else(|| {
CompileError::Codegen("not currently in a block".to_string())
})?;
self.builder.build_unconditional_branch(*frame.code_after());
for phi in frame.phis().to_vec().iter().rev() {
let (value, info) = self.state.pop1_extra()?;
let value = self.apply_pending_canonicalization(value, info);
phi.add_incoming(&[(&value, current_block)])
}
let frame = self.state.frame_at_depth(0)?;
self.builder.build_unconditional_branch(*frame.code_after());
}
let (if_else_block, if_else_state) = if let ControlFrame::IfElse {
@@ -1696,19 +1701,7 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
self.state.push1(phi.as_basic_value());
} else {
let basic_ty = phi.as_basic_value().get_type();
let placeholder_value = match basic_ty {
BasicTypeEnum::IntType(int_ty) => {
int_ty.const_int(0, false).as_basic_value_enum()
}
BasicTypeEnum::FloatType(float_ty) => {
float_ty.const_float(0.0).as_basic_value_enum()
}
_ => {
return Err(CompileError::Codegen(
"Operator::End phi type unimplemented".to_string(),
));
}
};
let placeholder_value = const_zero(basic_ty);
self.state.push1(placeholder_value);
phi.as_instruction().erase_from_basic_block();
}
@@ -1721,15 +1714,13 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
.ok_or_else(|| CompileError::Codegen("not currently in a block".to_string()))?;
let frame = self.state.outermost_frame()?;
self.builder.build_unconditional_branch(*frame.br_dest());
for phi in frame.phis().to_vec().iter().rev() {
let (arg, info) = self.state.pop1_extra()?;
let arg = self.apply_pending_canonicalization(arg, info);
phi.add_incoming(&[(&arg, current_block)]);
}
let frame = self.state.outermost_frame()?;
self.builder.build_unconditional_branch(*frame.br_dest());
self.state.reachable = false;
}
@@ -2090,8 +2081,7 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
});
let params = abi::args_to_call(
// TODO: should be an alloca_builder.
&self.builder,
&self.alloca_builder,
func_type,
callee_vmctx.into_pointer_value(),
&func.get_type().get_element_type().into_function_type(),
@@ -2334,8 +2324,7 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
});
let params = abi::args_to_call(
// TODO: should be an alloca_builder.
&self.builder,
&self.alloca_builder,
func_type,
ctx_ptr.into_pointer_value(),
&llvm_func_type,

View File

@@ -1,6 +1,6 @@
[package]
name = "wasmer-compiler-singlepass"
version = "0.16.2"
version = "1.0.0-alpha.1"
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
description = "Standalone environment support for WebAsssembly code in Singlepass"
license = "MIT OR (Apache-2.0 WITH LLVM-exception)"
@@ -12,9 +12,9 @@ readme = "README.md"
edition = "2018"
[dependencies]
wasmer-compiler = { path = "../compiler", version = "0.16.2", features = ["translator"], default-features = false }
wasmer-runtime = { path = "../runtime", version = "0.16.2" }
wasm-common = { path = "../wasm-common", version = "0.16.2", default-features = false }
wasmer-compiler = { path = "../compiler", version = "1.0.0-alpha.1", features = ["translator"], default-features = false }
wasmer-runtime = { path = "../runtime", version = "1.0.0-alpha.1" }
wasm-common = { path = "../wasm-common", version = "1.0.0-alpha.1", default-features = false }
rayon = "1.3"
hashbrown = { version = "0.7", optional = true }
serde = { version = "1.0", features = ["derive"] }

View File

@@ -1236,7 +1236,6 @@ impl<'a> FuncGen<'a> {
MemoryStyle::Dynamic => true,
};
let tmp_addr = self.machine.acquire_temp_gpr().unwrap();
let tmp_base = self.machine.acquire_temp_gpr().unwrap();
// Reusing `tmp_addr` for temporary indirection here, since it's not used before the last reference to `{base,bound}_loc`.
let (base_loc, bound_loc) = if self.module.num_imported_memories != 0 {
@@ -1261,15 +1260,36 @@ impl<'a> FuncGen<'a> {
)
};
let tmp_base = self.machine.acquire_temp_gpr().unwrap();
let tmp_bound = self.machine.acquire_temp_gpr().unwrap();
// Load base into temporary register.
self.assembler
.emit_mov(Size::S64, base_loc, Location::GPR(tmp_base));
// Load bound into temporary register, if needed.
if need_check {
let tmp_bound = self.machine.acquire_temp_gpr().unwrap();
self.assembler
.emit_mov(Size::S32, bound_loc, Location::GPR(tmp_bound));
// Wasm -> Effective.
// Assuming we never underflow - should always be true on Linux/macOS and Windows >=8,
// since the first page from 0x0 to 0x1000 is not accepted by mmap.
// This `lea` calculates the upper bound allowed for the beginning of the word.
// Since the upper bound of the memory is (exclusively) `tmp_bound + tmp_base`,
// the maximum allowed beginning of word is (inclusively)
// `tmp_bound + tmp_base - value_size`.
self.assembler.emit_lea(
Size::S64,
Location::MemoryAddTriple(tmp_bound, tmp_base, -(value_size as i32)),
Location::GPR(tmp_bound),
);
}
// Load effective address.
// `base_loc` and `bound_loc` becomes INVALID after this line, because `tmp_addr`
// might be reused.
self.assembler
.emit_mov(Size::S32, addr, Location::GPR(tmp_addr));
@@ -1280,51 +1300,27 @@ impl<'a> FuncGen<'a> {
Location::Imm32(memarg.offset),
Location::GPR(tmp_addr),
);
// Overflow is checked outside the `need_check` block, so we don't need to check it here.
}
// Trap if the start address of the requested area is equal to or above that of the linear memory.
self.assembler
.emit_cmp(Size::S32, Location::GPR(tmp_bound), Location::GPR(tmp_addr));
self.assembler
.emit_jmp(Condition::AboveEqual, self.special_labels.heap_access_oob);
// Calculate end of word.
if value_size != 0 {
self.assembler.emit_add(
Size::S32,
Location::Imm32(value_size as u32),
Location::GPR(tmp_addr),
);
self.assembler
.emit_jmp(Condition::Carry, self.special_labels.heap_access_oob);
}
// Trap if the end address of the requested area is above that of the linear memory.
self.assembler
.emit_cmp(Size::S64, Location::GPR(tmp_bound), Location::GPR(tmp_addr));
self.assembler
.emit_jmp(Condition::Above, self.special_labels.heap_access_oob);
self.machine.release_temp_gpr(tmp_bound);
}
// Calculates the real address, and loads from it.
self.assembler
.emit_mov(Size::S32, addr, Location::GPR(tmp_addr));
if memarg.offset != 0 {
self.assembler.emit_add(
Size::S64,
Location::Imm32(memarg.offset as u32),
Location::GPR(tmp_addr),
);
// Trap if offset calculation overflowed.
self.assembler
.emit_jmp(Condition::Carry, self.special_labels.heap_access_oob);
}
// Wasm linear memory -> real memory
self.assembler
.emit_add(Size::S64, Location::GPR(tmp_base), Location::GPR(tmp_addr));
if need_check {
// Trap if the end address of the requested area is above that of the linear memory.
self.assembler
.emit_cmp(Size::S64, Location::GPR(tmp_bound), Location::GPR(tmp_addr));
// `tmp_bound` is inclusive. So trap only if `tmp_addr > tmp_bound`.
self.assembler
.emit_jmp(Condition::Above, self.special_labels.heap_access_oob);
}
self.machine.release_temp_gpr(tmp_bound);
self.machine.release_temp_gpr(tmp_base);
let align = match memarg.flags & 3 {

View File

@@ -20,6 +20,7 @@ pub enum Location {
GPR(GPR),
XMM(XMM),
Memory(GPR, i32),
MemoryAddTriple(GPR, GPR, i32),
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -796,6 +797,12 @@ impl Emitter for Assembler {
(Size::S64, Location::Memory(src, disp), Location::GPR(dst)) => {
dynasm!(self ; lea Rq(dst as u8), [Rq(src as u8) + disp]);
}
(Size::S32, Location::MemoryAddTriple(src1, src2, disp), Location::GPR(dst)) => {
dynasm!(self ; lea Rd(dst as u8), [Rq(src1 as u8) + Rq(src2 as u8) + disp]);
}
(Size::S64, Location::MemoryAddTriple(src1, src2, disp), Location::GPR(dst)) => {
dynasm!(self ; lea Rq(dst as u8), [Rq(src1 as u8) + Rq(src2 as u8) + disp]);
}
_ => panic!("singlepass can't emit LEA {:?} {:?} {:?}", sz, src, dst),
}
}

View File

@@ -1,6 +1,6 @@
[package]
name = "wasmer-compiler"
version = "0.16.2"
version = "1.0.0-alpha.1"
description = "Base compiler abstraction for WebAssembly"
license = "MIT OR (Apache-2.0 WITH LLVM-exception)"
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
@@ -11,8 +11,8 @@ keywords = ["webassembly", "wasm", "compiler"]
edition = "2018"
[dependencies]
wasmer-runtime = { path = "../runtime", version = "0.16.2" }
wasm-common = { path = "../wasm-common", version = "0.16.2" }
wasmer-runtime = { path = "../runtime", version = "1.0.0-alpha.1" }
wasm-common = { path = "../wasm-common", version = "1.0.0-alpha.1" }
wasmparser = { version = "0.51", optional = true, default-features = false }
target-lexicon = { version = "0.10", default-features = false }
enumset = "1.0"

View File

@@ -129,19 +129,19 @@ impl FromStr for CpuFeature {
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"sse2" => Ok(CpuFeature::SSE2),
"sse3" => Ok(CpuFeature::SSE3),
"ssse3" => Ok(CpuFeature::SSSE3),
"sse4.1" => Ok(CpuFeature::SSE41),
"sse4.2" => Ok(CpuFeature::SSE42),
"popcnt" => Ok(CpuFeature::POPCNT),
"avx" => Ok(CpuFeature::AVX),
"bmi" => Ok(CpuFeature::BMI1),
"bmi2" => Ok(CpuFeature::BMI2),
"avx2" => Ok(CpuFeature::AVX2),
"avx512dq" => Ok(CpuFeature::AVX512DQ),
"avx512vl" => Ok(CpuFeature::AVX512VL),
"lzcnt" => Ok(CpuFeature::LZCNT),
"sse2" => Ok(Self::SSE2),
"sse3" => Ok(Self::SSE3),
"ssse3" => Ok(Self::SSSE3),
"sse4.1" => Ok(Self::SSE41),
"sse4.2" => Ok(Self::SSE42),
"popcnt" => Ok(Self::POPCNT),
"avx" => Ok(Self::AVX),
"bmi" => Ok(Self::BMI1),
"bmi2" => Ok(Self::BMI2),
"avx2" => Ok(Self::AVX2),
"avx512dq" => Ok(Self::AVX512DQ),
"avx512vl" => Ok(Self::AVX512VL),
"lzcnt" => Ok(Self::LZCNT),
_ => Err(ParseCpuFeatureError::Missing(s.to_string())),
}
}
@@ -150,19 +150,19 @@ impl FromStr for CpuFeature {
impl ToString for CpuFeature {
fn to_string(&self) -> String {
match self {
CpuFeature::SSE2 => "sse2",
CpuFeature::SSE3 => "sse3",
CpuFeature::SSSE3 => "ssse3",
CpuFeature::SSE41 => "sse4.1",
CpuFeature::SSE42 => "sse4.2",
CpuFeature::POPCNT => "popcnt",
CpuFeature::AVX => "avx",
CpuFeature::BMI1 => "bmi",
CpuFeature::BMI2 => "bmi2",
CpuFeature::AVX2 => "avx2",
CpuFeature::AVX512DQ => "avx512dq",
CpuFeature::AVX512VL => "avx512vl",
CpuFeature::LZCNT => "lzcnt",
Self::SSE2 => "sse2",
Self::SSE3 => "sse3",
Self::SSSE3 => "ssse3",
Self::SSE41 => "sse4.1",
Self::SSE42 => "sse4.2",
Self::POPCNT => "popcnt",
Self::AVX => "avx",
Self::BMI1 => "bmi",
Self::BMI2 => "bmi2",
Self::AVX2 => "avx2",
Self::AVX512DQ => "avx512dq",
Self::AVX512VL => "avx512vl",
Self::LZCNT => "lzcnt",
}
.to_string()
}

View File

@@ -2,7 +2,6 @@ use crate::{wasm_unsupported, WasmResult};
use std::boxed::Box;
use wasm_common::entity::PrimaryMap;
use wasm_common::SignatureIndex;
use wasmparser;
/// Map of signatures to a function's parameter and return types.
pub(crate) type WasmTypes =

View File

@@ -1,6 +1,6 @@
[package]
name = "wasmer-emscripten"
version = "0.16.2"
version = "1.0.0-alpha.1"
description = "Wasmer runtime emscripten implementation library"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@@ -15,7 +15,7 @@ lazy_static = "1.4"
libc = "0.2.60"
log = "0.4"
time = "0.1"
wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" }
wasmer-runtime-core = { path = "../runtime-core", version = "1.0.0-alpha.1" }
[target.'cfg(windows)'.dependencies]
getrandom = "0.1"

View File

@@ -1,6 +1,6 @@
[package]
name = "wasmer-engine-jit"
version = "0.16.2"
version = "1.0.0-alpha.1"
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
description = "Wasmer JIT Engine"
license = "(Apache-2.0 WITH LLVM-exception) or MIT"
@@ -11,10 +11,10 @@ readme = "README.md"
edition = "2018"
[dependencies]
wasm-common = { path = "../wasm-common", version = "0.16.2" }
wasmer-compiler = { path = "../compiler", version = "0.16.2", default-features = false }
wasmer-runtime = { path = "../runtime", version = "0.16.2" }
wasmer-engine = { path = "../engine", version = "0.16.2" }
wasm-common = { path = "../wasm-common", version = "1.0.0-alpha.1" }
wasmer-compiler = { path = "../compiler", version = "1.0.0-alpha.1", default-features = false }
wasmer-runtime = { path = "../runtime", version = "1.0.0-alpha.1" }
wasmer-engine = { path = "../engine", version = "1.0.0-alpha.1" }
# flexbuffers = { path = "../../../flatbuffers/rust/flexbuffers", version = "0.1.0" }
region = "2.1"
serde = { version = "1.0", features = ["derive", "rc"] }

View File

@@ -3,7 +3,9 @@
use crate::engine::{JITEngine, JITEngineInner};
use crate::link::link_module;
use crate::serialize::{SerializableCompilation, SerializableModule};
#[cfg(feature = "compiler")]
use crate::serialize::SerializableCompilation;
use crate::serialize::SerializableModule;
use std::sync::{Arc, Mutex};
use wasm_common::entity::{BoxedSlice, PrimaryMap};
use wasm_common::{
@@ -14,12 +16,11 @@ use wasm_common::{
use wasmer_compiler::ModuleEnvironment;
use wasmer_compiler::{CompileError, Features};
use wasmer_engine::{
register_frame_info, Artifact, DeserializeError, Engine, GlobalFrameInfoRegistration,
SerializableFunctionFrameInfo, SerializeError,
register_frame_info, Artifact, DeserializeError, GlobalFrameInfoRegistration, SerializeError,
};
use wasmer_runtime::{ModuleInfo, VMFunctionBody, VMSharedSignatureIndex};
use wasmer_runtime::{MemoryPlan, TablePlan};
#[cfg(feature = "compiler")]
use wasmer_engine::{Engine, SerializableFunctionFrameInfo};
use wasmer_runtime::{MemoryPlan, ModuleInfo, TablePlan, VMFunctionBody, VMSharedSignatureIndex};
/// A compiled wasm module, ready to be instantiated.
pub struct JITArtifact {
@@ -28,7 +29,7 @@ pub struct JITArtifact {
finished_functions: BoxedSlice<LocalFunctionIndex, *mut [VMFunctionBody]>,
finished_dynamic_function_trampolines: BoxedSlice<FunctionIndex, *const VMFunctionBody>,
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
frame_info_registration: Mutex<Option<Option<GlobalFrameInfoRegistration>>>,
frame_info_registration: Mutex<Option<GlobalFrameInfoRegistration>>,
}
impl JITArtifact {
@@ -132,7 +133,7 @@ impl JITArtifact {
/// Compile a data buffer into a `JITArtifact`, which may then be instantiated.
#[cfg(not(feature = "compiler"))]
pub fn new(jit: &JITEngine, data: &[u8]) -> Result<Self, CompileError> {
pub fn new(_jit: &JITEngine, _data: &[u8]) -> Result<Self, CompileError> {
Err(CompileError::Codegen(
"Compilation is not enabled in the engine".to_string(),
))
@@ -224,16 +225,18 @@ impl Artifact for JITArtifact {
fn register_frame_info(&self) {
let mut info = self.frame_info_registration.lock().unwrap();
if info.is_some() {
return;
}
let frame_infos = &self.serializable.compilation.function_frame_info;
let finished_functions = &self.finished_functions;
*info = Some(register_frame_info(
*info = register_frame_info(
self.serializable.module.clone(),
finished_functions,
frame_infos.clone(),
));
);
}
fn features(&self) -> &Features {

View File

@@ -1,6 +1,5 @@
//! Memory management for executable code.
use crate::function_table::FunctionTable;
use region;
use std::mem::ManuallyDrop;
use std::{cmp, mem};
use wasm_common::entity::PrimaryMap;

View File

@@ -10,7 +10,7 @@ use wasmer_compiler::{
};
#[cfg(feature = "compiler")]
use wasmer_compiler::{Compiler, CompilerConfig};
use wasmer_engine::{Artifact, DeserializeError, Engine, Tunables};
use wasmer_engine::{Artifact, DeserializeError, Engine, EngineId, Tunables};
use wasmer_runtime::{
ModuleInfo, SignatureRegistry, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline,
};
@@ -20,6 +20,7 @@ use wasmer_runtime::{
pub struct JITEngine {
inner: Arc<Mutex<JITEngineInner>>,
tunables: Arc<dyn Tunables + Send + Sync>,
engine_id: EngineId,
}
impl JITEngine {
@@ -38,6 +39,7 @@ impl JITEngine {
signatures: SignatureRegistry::new(),
})),
tunables: Arc::new(tunables),
engine_id: EngineId::default(),
}
}
@@ -64,6 +66,7 @@ impl JITEngine {
signatures: SignatureRegistry::new(),
})),
tunables: Arc::new(tunables),
engine_id: EngineId::default(),
}
}
@@ -113,6 +116,10 @@ impl Engine for JITEngine {
unsafe fn deserialize(&self, bytes: &[u8]) -> Result<Arc<dyn Artifact>, DeserializeError> {
Ok(Arc::new(JITArtifact::deserialize(&self, &bytes)?))
}
fn id(&self) -> &EngineId {
&self.engine_id
}
}
/// The inner contents of `JITEngine`
@@ -131,7 +138,7 @@ pub struct JITEngineInner {
}
impl JITEngineInner {
/// Gets the compiler associated to this JIT
/// Gets the compiler associated to this engine.
#[cfg(feature = "compiler")]
pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> {
if self.compiler.is_none() {
@@ -148,9 +155,10 @@ impl JITEngineInner {
/// Validate the module
#[cfg(not(feature = "compiler"))]
pub fn validate<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> {
pub fn validate<'data>(&self, _data: &'data [u8]) -> Result<(), CompileError> {
Err(CompileError::Validate(
"Validation is only enabled with the compiler feature".to_string(),
"The JITEngine is not compiled with compiler support, which is required for validating"
.to_string(),
))
}
@@ -178,6 +186,7 @@ impl JITEngineInner {
}
/// Compile the given function bodies.
#[allow(clippy::type_complexity)]
pub(crate) fn allocate(
&mut self,
module: &ModuleInfo,

View File

@@ -1,6 +1,6 @@
[package]
name = "wasmer-engine-native"
version = "0.16.2"
version = "1.0.0-alpha.1"
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
description = "Wasmer JIT Engine"
license = "(Apache-2.0 WITH LLVM-exception) or MIT"
@@ -11,10 +11,10 @@ readme = "README.md"
edition = "2018"
[dependencies]
wasm-common = { path = "../wasm-common", version = "0.16.2" }
wasmer-compiler = { path = "../compiler", version = "0.16.2", default-features = false }
wasmer-runtime = { path = "../runtime", version = "0.16.2" }
wasmer-engine = { path = "../engine", version = "0.16.2" }
wasm-common = { path = "../wasm-common", version = "1.0.0-alpha.1" }
wasmer-compiler = { path = "../compiler", version = "1.0.0-alpha.1", default-features = false }
wasmer-runtime = { path = "../runtime", version = "1.0.0-alpha.1" }
wasmer-engine = { path = "../engine", version = "1.0.0-alpha.1" }
faerie = "0.15"
serde = { version = "1.0", features = ["derive", "rc"] }
serde_bytes = { version = "0.11" }

View File

@@ -3,25 +3,31 @@
use crate::engine::{NativeEngine, NativeEngineInner};
use crate::serialize::ModuleMetadata;
#[cfg(feature = "compiler")]
use faerie::{ArtifactBuilder, Decl, Link};
use libloading::{Library, Symbol};
use std::error::Error;
use std::fs::File;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
#[cfg(feature = "compiler")]
use std::process::Command;
use std::sync::Arc;
use tempfile::NamedTempFile;
#[cfg(feature = "compiler")]
use tracing::trace;
use wasm_common::entity::{BoxedSlice, EntityRef, PrimaryMap};
use wasm_common::{
FunctionIndex, LocalFunctionIndex, MemoryIndex, OwnedDataInitializer, SignatureIndex,
TableIndex,
};
#[cfg(feature = "compiler")]
use wasmer_compiler::ModuleEnvironment;
use wasmer_compiler::{BinaryFormat, CompileError, Features, RelocationTarget, Triple};
use wasmer_compiler::{BinaryFormat, ModuleEnvironment, RelocationTarget, Triple};
use wasmer_compiler::{CompileError, Features};
#[cfg(feature = "compiler")]
use wasmer_engine::Engine;
use wasmer_engine::{
Artifact, DeserializeError, Engine, InstantiationError, LinkError, RuntimeError, SerializeError,
Artifact, DeserializeError, InstantiationError, LinkError, RuntimeError, SerializeError,
};
use wasmer_runtime::{MemoryPlan, TablePlan};
use wasmer_runtime::{ModuleInfo, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline};
@@ -61,13 +67,13 @@ impl NativeArtifact {
pub fn is_deserializable(bytes: &[u8]) -> bool {
cfg_if::cfg_if! {
if #[cfg(all(target_pointer_width = "64", target_os="macos"))] {
return bytes.starts_with(Self::MAGIC_HEADER_MH_CIGAM_64);
bytes.starts_with(Self::MAGIC_HEADER_MH_CIGAM_64)
}
else if #[cfg(all(target_pointer_width = "64", target_os="linux"))] {
return bytes.starts_with(Self::MAGIC_HEADER_ELF_64);
bytes.starts_with(Self::MAGIC_HEADER_ELF_64)
}
else if #[cfg(all(target_pointer_width = "32", target_os="linux"))] {
return bytes.starts_with(Self::MAGIC_HEADER_ELF_32);
bytes.starts_with(Self::MAGIC_HEADER_ELF_32)
}
else {
false
@@ -82,9 +88,7 @@ impl NativeArtifact {
let mut engine_inner = engine.inner_mut();
let tunables = engine.tunables();
let translation = environ
.translate(data)
.map_err(|error| CompileError::Wasm(error))?;
let translation = environ.translate(data).map_err(CompileError::Wasm)?;
let memory_plans: PrimaryMap<MemoryIndex, MemoryPlan> = translation
.module
@@ -290,38 +294,38 @@ impl NativeArtifact {
let shared_file = NamedTempFile::new().map_err(to_compile_error)?;
let (_file, shared_filepath) = shared_file.keep().map_err(to_compile_error)?;
let wasmer_symbols = libcalls
.iter()
.map(|libcall| {
match target_triple.binary_format {
BinaryFormat::Macho => format!("-Wl,-U,_{}", libcall),
BinaryFormat::Elf => format!("-Wl,--undefined={}", libcall),
_ => {
// We should already be filtering only valid binary formats before
// so this should never happen.
unreachable!("Incorrect binary format")
}
}
})
.collect::<Vec<String>>();
let is_cross_compiling = target_triple != Triple::host();
let host_target = Triple::host();
let is_cross_compiling = target_triple != host_target;
let cross_compiling_args = if is_cross_compiling {
vec![
format!("--target={}", target_triple),
"-fuse-ld=lld".to_string(),
"-nodefaultlibs".to_string(),
"-nostdlib".to_string(),
]
} else {
vec![]
};
let output = Command::new("gcc")
trace!(
"Compiling for target {} from host {}",
target_triple.to_string(),
host_target.to_string()
);
let linker = if is_cross_compiling {
"clang-10"
} else {
"gcc"
};
let output = Command::new(linker)
.arg(&filepath)
.arg("-nostartfiles")
.arg("-nodefaultlibs")
.arg("-nostdlib")
.arg("-o")
.arg(&shared_filepath)
.args(&wasmer_symbols)
.arg("-Wl,-undefined,dynamic_lookup")
// .args(&wasmer_symbols)
.arg("-shared")
.args(&cross_compiling_args)
.arg("-v")
@@ -415,7 +419,7 @@ impl NativeArtifact {
// implemented in this engine.
let func_pointer =
std::slice::from_raw_parts(raw as *const (), *function_len as usize);
let func_pointer = std::mem::transmute::<_, *mut [VMFunctionBody]>(func_pointer);
let func_pointer = func_pointer as *const [()] as *mut [VMFunctionBody];
finished_functions.push(func_pointer);
}
}
@@ -489,17 +493,21 @@ impl NativeArtifact {
/// Compile a data buffer into a `NativeArtifact`, which may then be instantiated.
#[cfg(not(feature = "compiler"))]
pub fn new(engine: &NativeEngine, data: &[u8]) -> Result<Self, CompileError> {
pub fn new(_engine: &NativeEngine, _data: &[u8]) -> Result<Self, CompileError> {
Err(CompileError::Codegen(
"Compilation is not enabled in the engine".to_string(),
))
}
/// Deserialize a `NativeArtifact` from bytes.
///
/// # Safety
///
/// The bytes must represent a serialized WebAssembly module.
pub unsafe fn deserialize(
engine: &NativeEngine,
bytes: &[u8],
) -> Result<NativeArtifact, DeserializeError> {
) -> Result<Self, DeserializeError> {
if !Self::is_deserializable(&bytes) {
return Err(DeserializeError::Incompatible(
"The provided bytes are not in any native format Wasmer can understand".to_string(),
@@ -515,10 +523,14 @@ impl NativeArtifact {
}
/// Deserialize a `NativeArtifact` from a file path.
///
/// # Safety
///
/// The file's content must represent a serialized WebAssembly module.
pub unsafe fn deserialize_from_file(
engine: &NativeEngine,
path: &Path,
) -> Result<NativeArtifact, DeserializeError> {
) -> Result<Self, DeserializeError> {
let mut file = File::open(&path)?;
let mut buffer = [0; 5];
// read up to 5 bytes
@@ -532,10 +544,14 @@ impl NativeArtifact {
}
/// Deserialize a `NativeArtifact` from a file path (unchecked).
///
/// # Safety
///
/// The file's content must represent a serialized WebAssembly module.
pub unsafe fn deserialize_from_file_unchecked(
engine: &NativeEngine,
path: &Path,
) -> Result<NativeArtifact, DeserializeError> {
) -> Result<Self, DeserializeError> {
let lib = Library::new(&path).map_err(|e| {
DeserializeError::CorruptedBinary(format!("Library loading failed: {}", e))
})?;
@@ -565,7 +581,7 @@ impl NativeArtifact {
let mut engine_inner = engine.inner_mut();
Self::from_parts(&mut engine_inner, metadata, shared_path, lib)
.map_err(|e| DeserializeError::Compiler(e))
.map_err(DeserializeError::Compiler)
}
}

View File

@@ -9,7 +9,7 @@ use wasm_common::FunctionType;
use wasmer_compiler::CompileError;
#[cfg(feature = "compiler")]
use wasmer_compiler::{Compiler, CompilerConfig};
use wasmer_engine::{Artifact, DeserializeError, Engine, Tunables};
use wasmer_engine::{Artifact, DeserializeError, Engine, EngineId, Tunables};
use wasmer_runtime::{SignatureRegistry, VMSharedSignatureIndex, VMTrampoline};
/// A WebAssembly `Native` Engine.
@@ -17,6 +17,7 @@ use wasmer_runtime::{SignatureRegistry, VMSharedSignatureIndex, VMTrampoline};
pub struct NativeEngine {
inner: Arc<Mutex<NativeEngineInner>>,
tunables: Arc<dyn Tunables + Send + Sync>,
engine_id: EngineId,
}
impl NativeEngine {
@@ -36,6 +37,7 @@ impl NativeEngine {
prefixer: None,
})),
tunables: Arc::new(tunables),
engine_id: EngineId::default(),
}
}
@@ -62,6 +64,7 @@ impl NativeEngine {
prefixer: None,
})),
tunables: Arc::new(tunables),
engine_id: EngineId::default(),
}
}
@@ -140,6 +143,10 @@ impl Engine for NativeEngine {
&self, &file_ref,
)?))
}
fn id(&self) -> &EngineId {
&self.engine_id
}
}
/// The inner contents of `NativeEngine`
@@ -159,7 +166,7 @@ pub struct NativeEngineInner {
}
impl NativeEngineInner {
/// Gets the compiler associated to this JIT
/// Gets the compiler associated to this engine.
#[cfg(feature = "compiler")]
pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> {
if self.compiler.is_none() {
@@ -187,9 +194,9 @@ impl NativeEngineInner {
/// Validate the module
#[cfg(not(feature = "compiler"))]
pub fn validate<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> {
pub fn validate<'data>(&self, _data: &'data [u8]) -> Result<(), CompileError> {
Err(CompileError::Validate(
"Validation is only enabled with the compiler feature".to_string(),
"The NativeEngine is not compiled with compiler support, which is required for validating".to_string(),
))
}

View File

@@ -25,9 +25,6 @@
)
)]
#[macro_use]
extern crate tracing;
mod artifact;
mod engine;
mod serialize;

View File

@@ -1,6 +1,6 @@
[package]
name = "wasmer-engine"
version = "0.16.2"
version = "1.0.0-alpha.1"
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
description = "Wasmer Engine abstraction"
license = "(Apache-2.0 WITH LLVM-exception) or MIT"
@@ -11,9 +11,9 @@ readme = "README.md"
edition = "2018"
[dependencies]
wasm-common = { path = "../wasm-common", version = "0.16.2" }
wasmer-compiler = { path = "../compiler", version = "0.16.2", default-features = false }
wasmer-runtime = { path = "../runtime", version = "0.16.2" }
wasm-common = { path = "../wasm-common", version = "1.0.0-alpha.1" }
wasmer-compiler = { path = "../compiler", version = "1.0.0-alpha.1", default-features = false }
wasmer-runtime = { path = "../runtime", version = "1.0.0-alpha.1" }
target-lexicon = { version = "0.10", default-features = false }
# flexbuffers = { path = "../../../flatbuffers/rust/flexbuffers", version = "0.1.0" }
backtrace = "0.3"

View File

@@ -80,7 +80,7 @@ pub trait Artifact {
/// Crate an `Instance` from this `Artifact`.
///
/// # Unsafety
/// # Safety
///
/// See [`InstanceHandle::new`].
unsafe fn instantiate(
@@ -89,7 +89,8 @@ pub trait Artifact {
resolver: &dyn Resolver,
host_state: Box<dyn Any>,
) -> Result<InstanceHandle, InstantiationError> {
let _ = self.preinstantiate()?;
self.preinstantiate()?;
let module = self.module();
let imports = resolve_imports(
&module,
@@ -129,7 +130,7 @@ pub trait Artifact {
/// Finishes the instantiation of a just created `InstanceHandle`.
///
/// # Unsafety
/// # Safety
///
/// See [`InstanceHandle::finish_instantiation`].
unsafe fn finish_instantiation(

View File

@@ -3,6 +3,7 @@
use crate::tunables::Tunables;
use crate::{Artifact, DeserializeError};
use std::path::Path;
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
use std::sync::Arc;
use wasm_common::FunctionType;
use wasmer_compiler::CompileError;
@@ -34,9 +35,17 @@ pub trait Engine {
fn compile(&self, binary: &[u8]) -> Result<Arc<dyn Artifact>, CompileError>;
/// Deserializes a WebAssembly module
///
/// # Safety
///
/// The serialized content must represent a serialized WebAssembly module.
unsafe fn deserialize(&self, bytes: &[u8]) -> Result<Arc<dyn Artifact>, DeserializeError>;
/// Deserializes a WebAssembly module from a path
///
/// # Safety
///
/// The file's content must represent a serialized WebAssembly module.
unsafe fn deserialize_from_file(
&self,
file_ref: &Path,
@@ -44,4 +53,40 @@ pub trait Engine {
let bytes = std::fs::read(file_ref)?;
self.deserialize(&bytes)
}
/// A unique identifier for this object.
///
/// This exists to allow us to compare two Engines for equality. Otherwise,
/// comparing two trait objects unsafely relies on implementation details
/// of trait representation.
fn id(&self) -> &EngineId;
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
/// A unique identifier for an Engine.
pub struct EngineId {
id: usize,
}
impl EngineId {
/// Format this identifier as a string.
pub fn id(&self) -> String {
format!("{}", &self.id)
}
}
impl Clone for EngineId {
fn clone(&self) -> Self {
Self::default()
}
}
impl Default for EngineId {
fn default() -> Self {
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
Self {
id: NEXT_ID.fetch_add(1, SeqCst),
}
}
}

View File

@@ -30,7 +30,7 @@ mod trap;
mod tunables;
pub use crate::artifact::Artifact;
pub use crate::engine::Engine;
pub use crate::engine::{Engine, EngineId};
pub use crate::error::{
DeserializeError, ImportError, InstantiationError, LinkError, SerializeError,
};

View File

@@ -1,6 +1,6 @@
[package]
name = "wasmer-runtime"
version = "0.16.2"
version = "1.0.0-alpha.1"
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
description = "Runtime library support for Wasmer"
license = "MIT OR (Apache-2.0 WITH LLVM-exception)"
@@ -11,7 +11,7 @@ readme = "README.md"
edition = "2018"
[dependencies]
wasm-common = { path = "../wasm-common", version = "0.16.2", features = ["enable-serde"] }
wasm-common = { path = "../wasm-common", version = "1.0.0-alpha.1", features = ["enable-serde"] }
region = "2.1"
libc = { version = "0.2", default-features = false }
memoffset = "0.5"

View File

@@ -1,6 +1,5 @@
use crate::memory::LinearMemory;
use crate::module::{MemoryPlan, TablePlan};
use crate::table::Table;
use crate::memory::{LinearMemory, MemoryPlan};
use crate::table::{Table, TablePlan};
use crate::vmcontext::{
VMContext, VMFunctionBody, VMFunctionKind, VMGlobalDefinition, VMMemoryDefinition,
VMTableDefinition,
@@ -85,7 +84,7 @@ impl ExportMemory {
}
/// Returns whether or not the two `ExportMemory`s refer to the same Memory.
pub fn same(&self, other: &ExportMemory) -> bool {
pub fn same(&self, other: &Self) -> bool {
self.definition == other.definition && self.from == other.from
}
}
@@ -107,7 +106,7 @@ pub struct ExportGlobal {
impl ExportGlobal {
/// Returns whether or not the two `ExportGlobal`s refer to the same Global.
pub fn same(&self, other: &ExportGlobal) -> bool {
pub fn same(&self, other: &Self) -> bool {
self.definition == other.definition && self.global == other.global
}
}

View File

@@ -587,8 +587,7 @@ impl Instance {
let passive_elements = self.passive_elements.borrow();
let elem = passive_elements
.get(&elem_index)
.map(|e| &**e)
.unwrap_or_else(|| &[]);
.map_or_else(|| -> &[VMCallerCheckedAnyfunc] { &[] }, |e| &**e);
if src
.checked_add(len)
@@ -765,7 +764,7 @@ pub struct InstanceHandle {
impl InstanceHandle {
/// Create a new `InstanceHandle` pointing at a new `Instance`.
///
/// # Unsafety
/// # Safety
///
/// This method is not necessarily inherently unsafe to call, but in general
/// the APIs of an `Instance` are quite unsafe and have not been really
@@ -778,6 +777,7 @@ impl InstanceHandle {
/// internally if you'd like to do so. If possible it's recommended to use
/// the `wasmer` crate API rather than this type since that is vetted for
/// safety.
#[allow(clippy::too_many_arguments)]
pub unsafe fn new(
module: Arc<ModuleInfo>,
finished_functions: BoxedSlice<LocalFunctionIndex, *mut [VMFunctionBody]>,
@@ -890,6 +890,8 @@ impl InstanceHandle {
/// Finishes the instantiation process started by `Instance::new`.
///
/// # Safety
///
/// Only safe to call immediately after instantiation.
pub unsafe fn finish_instantiation(
&self,
@@ -922,7 +924,8 @@ impl InstanceHandle {
/// This is unsafe because it doesn't work on just any `VMContext`, it must
/// be a `VMContext` allocated as part of an `Instance`.
pub unsafe fn from_vmctx(vmctx: *mut VMContext) -> Self {
let instance = (&mut *vmctx).instance();
let instance = (&*vmctx).instance();
Self {
instance: instance as *const Instance as *mut Instance,
}
@@ -1040,9 +1043,11 @@ impl InstanceHandle {
/// Deallocates memory associated with this instance.
///
/// Note that this is unsafe because there might be other handles to this
/// `InstanceHandle` elsewhere, and there's nothing preventing usage of
/// this handle after this function is called.
/// # Safety
///
/// This is unsafe because there might be other handles to this
/// `InstanceHandle` elsewhere, and there's nothing preventing
/// usage of this handle after this function is called.
pub unsafe fn dealloc(&self) {
let instance = self.instance();
let layout = instance.alloc_layout();

View File

@@ -39,15 +39,12 @@ pub mod libcalls;
pub use crate::export::*;
pub use crate::imports::Imports;
pub use crate::instance::InstanceHandle;
pub use crate::memory::{LinearMemory, MemoryError};
pub use crate::memory::{LinearMemory, MemoryError, MemoryPlan, MemoryStyle};
pub use crate::mmap::Mmap;
pub use crate::module::{
ExportsIterator, ImportsIterator, MemoryPlan, MemoryStyle, ModuleInfo, TableElements,
TablePlan, TableStyle,
};
pub use crate::module::{ExportsIterator, ImportsIterator, ModuleInfo};
pub use crate::probestack::PROBESTACK;
pub use crate::sig_registry::SignatureRegistry;
pub use crate::table::Table;
pub use crate::table::{Table, TableElements, TablePlan, TableStyle};
pub use crate::trap::*;
pub use crate::vmcontext::{
VMBuiltinFunctionIndex, VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionImportContext,

View File

@@ -131,12 +131,16 @@ pub extern "C" fn wasmer_f64_nearest(x: f64) -> f64 {
}
/// Implementation of memory.grow for locally-defined 32-bit memories.
///
/// # Safety
///
/// `vmctx` must be valid and not null.
pub unsafe extern "C" fn wasmer_memory32_grow(
vmctx: *mut VMContext,
delta: u32,
memory_index: u32,
) -> u32 {
let instance = (&mut *vmctx).instance();
let instance = (&*vmctx).instance();
let memory_index = LocalMemoryIndex::from_u32(memory_index);
instance
@@ -146,12 +150,16 @@ pub unsafe extern "C" fn wasmer_memory32_grow(
}
/// Implementation of memory.grow for imported 32-bit memories.
///
/// # Safety
///
/// `vmctx` must be valid and not null.
pub unsafe extern "C" fn wasmer_imported_memory32_grow(
vmctx: *mut VMContext,
delta: u32,
memory_index: u32,
) -> u32 {
let instance = (&mut *vmctx).instance();
let instance = (&*vmctx).instance();
let memory_index = MemoryIndex::from_u32(memory_index);
instance
@@ -161,25 +169,37 @@ pub unsafe extern "C" fn wasmer_imported_memory32_grow(
}
/// Implementation of memory.size for locally-defined 32-bit memories.
///
/// # Safety
///
/// `vmctx` must be valid and not null.
pub unsafe extern "C" fn wasmer_memory32_size(vmctx: *mut VMContext, memory_index: u32) -> u32 {
let instance = (&mut *vmctx).instance();
let instance = (&*vmctx).instance();
let memory_index = LocalMemoryIndex::from_u32(memory_index);
instance.memory_size(memory_index).0
}
/// Implementation of memory.size for imported 32-bit memories.
///
/// # Safety
///
/// `vmctx` must be valid and not null.
pub unsafe extern "C" fn wasmer_imported_memory32_size(
vmctx: *mut VMContext,
memory_index: u32,
) -> u32 {
let instance = (&mut *vmctx).instance();
let instance = (&*vmctx).instance();
let memory_index = MemoryIndex::from_u32(memory_index);
instance.imported_memory_size(memory_index).0
}
/// Implementation of `table.copy`.
///
/// # Safety
///
/// `vmctx` must be valid and not null.
pub unsafe extern "C" fn wasmer_table_copy(
vmctx: *mut VMContext,
dst_table_index: u32,
@@ -191,7 +211,7 @@ pub unsafe extern "C" fn wasmer_table_copy(
let result = {
let dst_table_index = TableIndex::from_u32(dst_table_index);
let src_table_index = TableIndex::from_u32(src_table_index);
let instance = (&mut *vmctx).instance();
let instance = (&*vmctx).instance();
let dst_table = instance.get_table(dst_table_index);
let src_table = instance.get_table(src_table_index);
Table::copy(dst_table, src_table, dst, src, len)
@@ -202,6 +222,10 @@ pub unsafe extern "C" fn wasmer_table_copy(
}
/// Implementation of `table.init`.
///
/// # Safety
///
/// `vmctx` must be valid and not null.
pub unsafe extern "C" fn wasmer_table_init(
vmctx: *mut VMContext,
table_index: u32,
@@ -213,7 +237,7 @@ pub unsafe extern "C" fn wasmer_table_init(
let result = {
let table_index = TableIndex::from_u32(table_index);
let elem_index = ElemIndex::from_u32(elem_index);
let instance = (&mut *vmctx).instance();
let instance = (&*vmctx).instance();
instance.table_init(table_index, elem_index, dst, src, len)
};
if let Err(trap) = result {
@@ -222,13 +246,21 @@ pub unsafe extern "C" fn wasmer_table_init(
}
/// Implementation of `elem.drop`.
///
/// # Safety
///
/// `vmctx` must be valid and not null.
pub unsafe extern "C" fn wasmer_elem_drop(vmctx: *mut VMContext, elem_index: u32) {
let elem_index = ElemIndex::from_u32(elem_index);
let instance = (&mut *vmctx).instance();
let instance = (&*vmctx).instance();
instance.elem_drop(elem_index);
}
/// Implementation of `memory.copy` for locally defined memories.
///
/// # Safety
///
/// `vmctx` must be valid and not null.
pub unsafe extern "C" fn wasmer_local_memory_copy(
vmctx: *mut VMContext,
memory_index: u32,
@@ -238,7 +270,7 @@ pub unsafe extern "C" fn wasmer_local_memory_copy(
) {
let result = {
let memory_index = LocalMemoryIndex::from_u32(memory_index);
let instance = (&mut *vmctx).instance();
let instance = (&*vmctx).instance();
instance.local_memory_copy(memory_index, dst, src, len)
};
if let Err(trap) = result {
@@ -247,6 +279,10 @@ pub unsafe extern "C" fn wasmer_local_memory_copy(
}
/// Implementation of `memory.copy` for imported memories.
///
/// # Safety
///
/// `vmctx` must be valid and not null.
pub unsafe extern "C" fn wasmer_imported_memory_copy(
vmctx: *mut VMContext,
memory_index: u32,
@@ -256,7 +292,7 @@ pub unsafe extern "C" fn wasmer_imported_memory_copy(
) {
let result = {
let memory_index = MemoryIndex::from_u32(memory_index);
let instance = (&mut *vmctx).instance();
let instance = (&*vmctx).instance();
instance.imported_memory_copy(memory_index, dst, src, len)
};
if let Err(trap) = result {
@@ -265,6 +301,10 @@ pub unsafe extern "C" fn wasmer_imported_memory_copy(
}
/// Implementation of `memory.fill` for locally defined memories.
///
/// # Safety
///
/// `vmctx` must be valid and not null.
pub unsafe extern "C" fn wasmer_memory_fill(
vmctx: *mut VMContext,
memory_index: u32,
@@ -274,7 +314,7 @@ pub unsafe extern "C" fn wasmer_memory_fill(
) {
let result = {
let memory_index = LocalMemoryIndex::from_u32(memory_index);
let instance = (&mut *vmctx).instance();
let instance = (&*vmctx).instance();
instance.local_memory_fill(memory_index, dst, val, len)
};
if let Err(trap) = result {
@@ -283,6 +323,10 @@ pub unsafe extern "C" fn wasmer_memory_fill(
}
/// Implementation of `memory.fill` for imported memories.
///
/// # Safety
///
/// `vmctx` must be valid and not null.
pub unsafe extern "C" fn wasmer_imported_memory_fill(
vmctx: *mut VMContext,
memory_index: u32,
@@ -292,7 +336,7 @@ pub unsafe extern "C" fn wasmer_imported_memory_fill(
) {
let result = {
let memory_index = MemoryIndex::from_u32(memory_index);
let instance = (&mut *vmctx).instance();
let instance = (&*vmctx).instance();
instance.imported_memory_fill(memory_index, dst, val, len)
};
if let Err(trap) = result {
@@ -301,6 +345,10 @@ pub unsafe extern "C" fn wasmer_imported_memory_fill(
}
/// Implementation of `memory.init`.
///
/// # Safety
///
/// `vmctx` must be valid and not null.
pub unsafe extern "C" fn wasmer_memory_init(
vmctx: *mut VMContext,
memory_index: u32,
@@ -312,7 +360,7 @@ pub unsafe extern "C" fn wasmer_memory_init(
let result = {
let memory_index = MemoryIndex::from_u32(memory_index);
let data_index = DataIndex::from_u32(data_index);
let instance = (&mut *vmctx).instance();
let instance = (&*vmctx).instance();
instance.memory_init(memory_index, data_index, dst, src, len)
};
if let Err(trap) = result {
@@ -321,13 +369,21 @@ pub unsafe extern "C" fn wasmer_memory_init(
}
/// Implementation of `data.drop`.
///
/// # Safety
///
/// `vmctx` must be valid and not null.
pub unsafe extern "C" fn wasmer_data_drop(vmctx: *mut VMContext, data_index: u32) {
let data_index = DataIndex::from_u32(data_index);
let instance = (&mut *vmctx).instance();
let instance = (&*vmctx).instance();
instance.data_drop(data_index)
}
/// Implementation for raising a trap
///
/// # Safety
///
/// To be defined (TODO)
#[no_mangle]
pub unsafe extern "C" fn wasmer_raise_trap(trap_code: TrapCode) -> ! {
let trap = Trap::wasm(trap_code);
@@ -335,6 +391,10 @@ pub unsafe extern "C" fn wasmer_raise_trap(trap_code: TrapCode) -> ! {
}
/// Probestack check
///
/// # Safety
///
/// To be defined (TODO)
#[no_mangle]
pub unsafe extern "C" fn wasmer_probestack() {
PROBESTACK();
@@ -345,53 +405,68 @@ pub unsafe extern "C" fn wasmer_probestack() {
/// This list is likely to grow over time.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum LibCall {
/// ceil.f32
CeilF32,
/// ceil.f64
CeilF64,
/// floor.f32
FloorF32,
/// floor.f64
FloorF64,
/// nearest.f32
NearestF32,
/// nearest.f64
NearestF64,
/// probe for stack overflow. These are emitted for functions which need
/// when the `enable_probestack` setting is true.
Probestack,
/// ceil.f32
CeilF32,
/// ceil.f64
CeilF64,
/// floor.f32
FloorF32,
/// floor.f64
FloorF64,
/// trunc.f32
TruncF32,
/// frunc.f64
TruncF64,
/// nearest.f32
NearestF32,
/// nearest.f64
NearestF64,
/// A custom trap
RaiseTrap,
// /// libc.memcpy
// Memcpy,
// /// libc.memset
// Memset,
// /// libc.memmove
// Memmove,
// /// Elf __tls_get_addr
// ElfTlsGetAddr,
/// trunc.f32
TruncF32,
/// frunc.f64
TruncF64,
}
impl LibCall {
/// The function pointer to a libcall
pub fn function_pointer(&self) -> usize {
match &self {
pub fn function_pointer(self) -> usize {
match self {
Self::CeilF32 => wasmer_f32_ceil as usize,
Self::FloorF32 => wasmer_f32_floor as usize,
Self::TruncF32 => wasmer_f32_trunc as usize,
Self::NearestF32 => wasmer_f32_nearest as usize,
Self::CeilF64 => wasmer_f64_ceil as usize,
Self::FloorF32 => wasmer_f32_floor as usize,
Self::FloorF64 => wasmer_f64_floor as usize,
Self::TruncF64 => wasmer_f64_trunc as usize,
Self::NearestF32 => wasmer_f32_nearest as usize,
Self::NearestF64 => wasmer_f64_nearest as usize,
Self::Probestack => wasmer_probestack as usize,
Self::RaiseTrap => wasmer_raise_trap as usize,
Self::Probestack => PROBESTACK as usize,
// other => panic!("unexpected libcall: {}", other),
Self::TruncF32 => wasmer_f32_trunc as usize,
Self::TruncF64 => wasmer_f64_trunc as usize,
}
}
/// Return the function name associated to the libcall.
pub fn to_function_name(&self) -> &str {
match self {
Self::CeilF32 => "wasmer_f32_ceil",
Self::CeilF64 => "wasmer_f64_ceil",
Self::FloorF32 => "wasmer_f32_floor",
Self::FloorF64 => "wasmer_f64_floor",
Self::NearestF32 => "wasmer_f32_nearest",
Self::NearestF64 => "wasmer_f64_nearest",
Self::Probestack => "wasmer_probestack",
Self::RaiseTrap => "wasmer_raise_trap",
Self::TruncF32 => "wasmer_f32_trunc",
Self::TruncF64 => "wasmer_f64_trunc",
}
}
}

View File

@@ -3,12 +3,12 @@
//! `LinearMemory` is to WebAssembly linear memories what `Table` is to WebAssembly tables.
use crate::mmap::Mmap;
use crate::module::{MemoryPlan, MemoryStyle};
use crate::vmcontext::VMMemoryDefinition;
use more_asserts::{assert_ge, assert_le};
use serde::{Deserialize, Serialize};
use std::cell::RefCell;
use thiserror::Error;
use wasm_common::{Bytes, Pages};
use wasm_common::{Bytes, MemoryType, Pages};
/// Error type describing things that can go wrong when operating on Wasm Memories.
#[derive(Error, Debug, Clone, PartialEq, Hash)]
@@ -36,6 +36,30 @@ pub enum MemoryError {
Generic(String),
}
/// Implementation styles for WebAssembly linear memory.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum MemoryStyle {
/// The actual memory can be resized and moved.
Dynamic,
/// Address space is allocated up front.
Static {
/// The number of mapped and unmapped pages.
bound: Pages,
},
}
/// A WebAssembly linear memory description along with our chosen style for
/// implementing it.
#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
pub struct MemoryPlan {
/// The WebAssembly linear memory description.
pub memory: MemoryType,
/// Our chosen implementation style.
pub style: MemoryStyle,
/// Our chosen offset-guard size.
pub offset_guard_size: u64,
}
/// A linear memory instance.
#[derive(Debug)]
pub struct LinearMemory {

View File

@@ -1,6 +1,7 @@
//! Data structure for representing WebAssembly modules
//! in a [`Module`].
use crate::table::TableElements;
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
@@ -12,64 +13,10 @@ use wasm_common::entity::{EntityRef, PrimaryMap};
use wasm_common::{
CustomSectionIndex, DataIndex, ElemIndex, ExportIndex, ExportType, ExternType, FunctionIndex,
FunctionType, GlobalIndex, GlobalInit, GlobalType, ImportIndex, ImportType, LocalFunctionIndex,
LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, MemoryType, Pages,
SignatureIndex, TableIndex, TableType,
LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, MemoryType, SignatureIndex,
TableIndex, TableType,
};
/// A WebAssembly table initializer.
#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
pub struct TableElements {
/// The index of a table to initialize.
pub table_index: TableIndex,
/// Optionally, a global variable giving a base index.
pub base: Option<GlobalIndex>,
/// The offset to add to the base.
pub offset: usize,
/// The values to write into the table elements.
pub elements: Box<[FunctionIndex]>,
}
/// Implementation styles for WebAssembly linear memory.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum MemoryStyle {
/// The actual memory can be resized and moved.
Dynamic,
/// Address space is allocated up front.
Static {
/// The number of mapped and unmapped pages.
bound: Pages,
},
}
/// A WebAssembly linear memory description along with our chosen style for
/// implementing it.
#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
pub struct MemoryPlan {
/// The WebAssembly linear memory description.
pub memory: MemoryType,
/// Our chosen implementation style.
pub style: MemoryStyle,
/// Our chosen offset-guard size.
pub offset_guard_size: u64,
}
/// Implementation styles for WebAssembly tables.
#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
pub enum TableStyle {
/// Signatures are stored in the table and checked in the caller.
CallerChecksSignature,
}
/// A WebAssembly table description along with our chosen style for
/// implementing it.
#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
pub struct TablePlan {
/// The WebAssembly table description.
pub table: TableType,
/// Our chosen implementation style.
pub style: TableStyle,
}
#[derive(Debug)]
pub struct ModuleId {
id: usize,
@@ -237,7 +184,7 @@ impl ModuleInfo {
}
ExportIndex::Global(i) => {
let global_type = self.globals.get(i.clone()).unwrap();
ExternType::Global(global_type.clone())
ExternType::Global(*global_type)
}
};
ExportType::new(name, extern_type)

View File

@@ -2,12 +2,42 @@
//!
//! `Table` is to WebAssembly tables what `LinearMemory` is to WebAssembly linear memories.
use crate::module::{TablePlan, TableStyle};
use crate::trap::{Trap, TrapCode};
use crate::vmcontext::{VMCallerCheckedAnyfunc, VMTableDefinition};
use serde::{Deserialize, Serialize};
use std::cell::RefCell;
use std::convert::{TryFrom, TryInto};
use wasm_common::Type;
use wasm_common::{FunctionIndex, GlobalIndex, TableIndex, TableType, Type};
/// A WebAssembly table initializer.
#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
pub struct TableElements {
/// The index of a table to initialize.
pub table_index: TableIndex,
/// Optionally, a global variable giving a base index.
pub base: Option<GlobalIndex>,
/// The offset to add to the base.
pub offset: usize,
/// The values to write into the table elements.
pub elements: Box<[FunctionIndex]>,
}
/// Implementation styles for WebAssembly tables.
#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
pub enum TableStyle {
/// Signatures are stored in the table and checked in the caller.
CallerChecksSignature,
}
/// A WebAssembly table description along with our chosen style for
/// implementing it.
#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
pub struct TablePlan {
/// The WebAssembly table description.
pub table: TableType,
/// Our chosen implementation style.
pub style: TableStyle,
}
/// A table instance.
#[derive(Debug)]

View File

@@ -415,6 +415,8 @@ impl Trap {
/// * `values_vec` - points to a buffer which holds the incoming arguments, and to
/// which the outgoing return values will be written.
///
/// # Safety
///
/// Wildly unsafe because it calls raw function pointers and reads/writes raw
/// function pointers.
pub unsafe fn wasmer_call_trampoline(
@@ -433,7 +435,9 @@ pub unsafe fn wasmer_call_trampoline(
/// Catches any wasm traps that happen within the execution of `closure`,
/// returning them as a `Result`.
///
/// Highly unsafe since `closure` won't have any dtors run.
/// # Safety
///
/// Highly unsafe since `closure` won't have any destructors run.
pub unsafe fn catch_traps<F>(vmctx: *mut VMContext, mut closure: F) -> Result<(), Trap>
where
F: FnMut(),
@@ -582,7 +586,7 @@ impl CallThreadState {
// First up see if any instance registered has a custom trap handler,
// in which case run them all. If anything handles the trap then we
// return that the trap was handled.
if self.any_instance(|i| {
let any_instance = self.any_instance(|i| {
let handler = match i.instance().signal_handler.replace(None) {
Some(handler) => handler,
None => return false,
@@ -590,7 +594,9 @@ impl CallThreadState {
let result = call_handler(&handler);
i.instance().signal_handler.set(Some(handler));
result
}) {
});
if any_instance {
self.handling_trap.set(false);
return 1 as *const _;
}

View File

@@ -433,120 +433,200 @@ impl VMGlobalDefinition {
}
/// Return a reference to the value as an i32.
///
/// # Safety
///
/// To be defined (TODO).
#[allow(clippy::cast_ptr_alignment)]
pub unsafe fn as_i32(&self) -> &i32 {
&*(self.storage.as_ref().as_ptr() as *const i32)
}
/// Return a mutable reference to the value as an i32.
///
/// # Safety
///
/// To be defined (TODO).
#[allow(clippy::cast_ptr_alignment)]
pub unsafe fn as_i32_mut(&mut self) -> &mut i32 {
&mut *(self.storage.as_mut().as_mut_ptr() as *mut i32)
}
/// Return a reference to the value as a u32.
///
/// # Safety
///
/// To be defined (TODO).
#[allow(clippy::cast_ptr_alignment)]
pub unsafe fn as_u32(&self) -> &u32 {
&*(self.storage.as_ref().as_ptr() as *const u32)
}
/// Return a mutable reference to the value as an u32.
///
/// # Safety
///
/// To be defined (TODO).
#[allow(clippy::cast_ptr_alignment)]
pub unsafe fn as_u32_mut(&mut self) -> &mut u32 {
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u32)
}
/// Return a reference to the value as an i64.
///
/// # Safety
///
/// To be defined (TODO).
#[allow(clippy::cast_ptr_alignment)]
pub unsafe fn as_i64(&self) -> &i64 {
&*(self.storage.as_ref().as_ptr() as *const i64)
}
/// Return a mutable reference to the value as an i64.
///
/// # Safety
///
/// To be defined (TODO).
#[allow(clippy::cast_ptr_alignment)]
pub unsafe fn as_i64_mut(&mut self) -> &mut i64 {
&mut *(self.storage.as_mut().as_mut_ptr() as *mut i64)
}
/// Return a reference to the value as an u64.
///
/// # Safety
///
/// To be defined (TODO).
#[allow(clippy::cast_ptr_alignment)]
pub unsafe fn as_u64(&self) -> &u64 {
&*(self.storage.as_ref().as_ptr() as *const u64)
}
/// Return a mutable reference to the value as an u64.
///
/// # Safety
///
/// To be defined (TODO).
#[allow(clippy::cast_ptr_alignment)]
pub unsafe fn as_u64_mut(&mut self) -> &mut u64 {
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u64)
}
/// Return a reference to the value as an f32.
///
/// # Safety
///
/// To be defined (TODO).
#[allow(clippy::cast_ptr_alignment)]
pub unsafe fn as_f32(&self) -> &f32 {
&*(self.storage.as_ref().as_ptr() as *const f32)
}
/// Return a mutable reference to the value as an f32.
///
/// # Safety
///
/// To be defined (TODO).
#[allow(clippy::cast_ptr_alignment)]
pub unsafe fn as_f32_mut(&mut self) -> &mut f32 {
&mut *(self.storage.as_mut().as_mut_ptr() as *mut f32)
}
/// Return a reference to the value as f32 bits.
///
/// # Safety
///
/// To be defined (TODO).
#[allow(clippy::cast_ptr_alignment)]
pub unsafe fn as_f32_bits(&self) -> &u32 {
&*(self.storage.as_ref().as_ptr() as *const u32)
}
/// Return a mutable reference to the value as f32 bits.
///
/// # Safety
///
/// To be defined (TODO).
#[allow(clippy::cast_ptr_alignment)]
pub unsafe fn as_f32_bits_mut(&mut self) -> &mut u32 {
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u32)
}
/// Return a reference to the value as an f64.
///
/// # Safety
///
/// To be defined (TODO).
#[allow(clippy::cast_ptr_alignment)]
pub unsafe fn as_f64(&self) -> &f64 {
&*(self.storage.as_ref().as_ptr() as *const f64)
}
/// Return a mutable reference to the value as an f64.
///
/// # Safety
///
/// To be defined (TODO).
#[allow(clippy::cast_ptr_alignment)]
pub unsafe fn as_f64_mut(&mut self) -> &mut f64 {
&mut *(self.storage.as_mut().as_mut_ptr() as *mut f64)
}
/// Return a reference to the value as f64 bits.
///
/// # Safety
///
/// To be defined (TODO).
#[allow(clippy::cast_ptr_alignment)]
pub unsafe fn as_f64_bits(&self) -> &u64 {
&*(self.storage.as_ref().as_ptr() as *const u64)
}
/// Return a mutable reference to the value as f64 bits.
///
/// # Safety
///
/// To be defined (TODO).
#[allow(clippy::cast_ptr_alignment)]
pub unsafe fn as_f64_bits_mut(&mut self) -> &mut u64 {
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u64)
}
/// Return a reference to the value as an u128.
///
/// # Safety
///
/// To be defined (TODO).
#[allow(clippy::cast_ptr_alignment)]
pub unsafe fn as_u128(&self) -> &u128 {
&*(self.storage.as_ref().as_ptr() as *const u128)
}
/// Return a mutable reference to the value as an u128.
///
/// # Safety
///
/// To be defined (TODO).
#[allow(clippy::cast_ptr_alignment)]
pub unsafe fn as_u128_mut(&mut self) -> &mut u128 {
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u128)
}
/// Return a reference to the value as u128 bits.
///
/// # Safety
///
/// To be defined (TODO).
#[allow(clippy::cast_ptr_alignment)]
pub unsafe fn as_u128_bits(&self) -> &[u8; 16] {
&*(self.storage.as_ref().as_ptr() as *const [u8; 16])
}
/// Return a mutable reference to the value as u128 bits.
///
/// # Safety
///
/// To be defined (TODO).
#[allow(clippy::cast_ptr_alignment)]
pub unsafe fn as_u128_bits_mut(&mut self) -> &mut [u8; 16] {
&mut *(self.storage.as_mut().as_mut_ptr() as *mut [u8; 16])
@@ -721,7 +801,7 @@ impl VMBuiltinFunctionIndex {
}
/// Return the index as an u32 number.
pub const fn index(&self) -> u32 {
pub const fn index(self) -> u32 {
self.0
}
}

View File

@@ -1,6 +1,6 @@
[package]
name = "wasmer-wasi-experimental-io-devices"
version = "0.16.2"
version = "1.0.0-alpha.1"
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
edition = "2018"
repository = "https://github.com/wasmerio/wasmer"
@@ -12,7 +12,7 @@ license = "MIT"
maintenance = { status = "experimental" }
[dependencies]
wasmer-wasi = { version = "0.16.2", path = "../wasi" }
wasmer-wasi = { version = "1.0.0-alpha.1", path = "../wasi" }
tracing = "0.1"
minifb = "0.16"
ref_thread_local = "0.0"

View File

@@ -1,6 +1,6 @@
[package]
name = "wasmer-wasi"
version = "0.16.2"
version = "1.0.0-alpha.1"
description = "Wasmer runtime WASI implementation library"
license = "MIT"
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
@@ -20,7 +20,7 @@ getrandom = "0.1"
time = "0.1"
typetag = "0.1"
serde = { version = "1.0", features = ["derive"] }
wasmer = { path = "../api", version = "0.16.2", default-features = false }
wasmer = { path = "../api", version = "1.0.0-alpha.1", default-features = false }
[target.'cfg(windows)'.dependencies]
winapi = "0.3"

View File

@@ -26,7 +26,7 @@ pub use crate::syscalls::types;
pub use crate::utils::{get_wasi_version, is_wasi_module, WasiVersion};
use thiserror::Error;
use wasmer::{imports, Function, ImportObject, Memory, Store};
use wasmer::{imports, Function, ImportObject, Memory, Module, Store};
/// This is returned in `RuntimeError`.
/// Use `downcast` or `downcast_ref` to retrieve the `ExitCode`.
@@ -34,6 +34,8 @@ use wasmer::{imports, Function, ImportObject, Memory, Store};
pub enum WasiError {
#[error("WASI exited with code: {0}")]
Exit(syscalls::types::__wasi_exitcode_t),
#[error("The WASI version could not be determined")]
UnknownWasiVersion,
}
/// The environment provided to the WASI imports.
@@ -51,6 +53,15 @@ impl<'a> WasiEnv<'a> {
}
}
pub fn import_object(&mut self, module: &Module) -> Result<ImportObject, WasiError> {
let wasi_version = get_wasi_version(module, false).ok_or(WasiError::UnknownWasiVersion)?;
Ok(generate_import_object_from_env(
module.store(),
self,
wasi_version,
))
}
/// Set the state
pub fn set_memory(&mut self, memory: &'a Memory) {
self.memory = Some(memory);

View File

@@ -13,6 +13,7 @@ impl<T: Copy, Ty> Copy for WasmPtr<T, Ty> {}
impl<T: Copy, Ty> Clone for WasmPtr<T, Ty> {
fn clone(&self) -> Self {
#[allow(clippy::clone_on_copy)]
Self(self.0.clone())
}
}
@@ -73,7 +74,7 @@ impl<T: Copy + ValueType> WasmPtr<T, Array> {
}
#[inline(always)]
pub fn get_utf8_string<'a>(self, memory: &'a Memory, str_len: u32) -> Option<&'a str> {
pub fn get_utf8_string(self, memory: &Memory, str_len: u32) -> Option<&str> {
self.0.get_utf8_string(memory, str_len)
}
}

View File

@@ -2,6 +2,7 @@
use crate::state::{WasiFile, WasiFs, WasiFsError, WasiState};
use crate::syscalls::types::{__WASI_STDERR_FILENO, __WASI_STDIN_FILENO, __WASI_STDOUT_FILENO};
use crate::WasiEnv;
use std::path::{Path, PathBuf};
use thiserror::Error;
@@ -36,6 +37,7 @@ pub struct WasiStateBuilder {
args: Vec<Vec<u8>>,
envs: Vec<Vec<u8>>,
preopens: Vec<PreopenedDir>,
#[allow(clippy::type_complexity)]
setup_fs_fn: Option<Box<dyn Fn(&mut WasiFs) -> Result<(), String> + Send>>,
stdout_override: Option<Box<dyn WasiFile>>,
stderr_override: Option<Box<dyn WasiFile>>,
@@ -78,16 +80,11 @@ pub enum WasiStateCreationError {
}
fn validate_mapped_dir_alias(alias: &str) -> Result<(), WasiStateCreationError> {
for byte in alias.bytes() {
match byte {
b'\0' => {
if !alias.bytes().all(|b| b != b'\0') {
return Err(WasiStateCreationError::MappedDirAliasFormattingError(
format!("Alias \"{}\" contains a nul byte", alias),
));
}
_ => (),
}
}
Ok(())
}
@@ -392,6 +389,14 @@ impl WasiStateBuilder {
envs: self.envs.clone(),
})
}
/// Consumes the [`WasiStateBuilder`] and produces a [`WasiEnv`]
///
/// Returns the error from `WasiFs::new` if there's an error
pub fn finalize(&mut self) -> Result<WasiEnv, WasiStateCreationError> {
let state = self.build()?;
Ok(WasiEnv::new(state))
}
}
/// Builder for preopened directories.

View File

@@ -13,6 +13,8 @@
//! You can implement `WasiFile` for your own types to get custom behavior and extend WASI, see the
//! [WASI plugin example](https://github.com/wasmerio/wasmer/blob/master/examples/plugin.rs).
#![allow(clippy::cognitive_complexity, clippy::too_many_arguments)]
mod builder;
mod types;
@@ -1357,7 +1359,7 @@ impl WasiFs {
_ => unreachable!("Symlink pointing to something that's not a directory as its base preopened directory"),
}
}
__ => return None,
_ => return None,
};
Some(__wasi_filestat_t {
st_filetype: host_file_type_to_wasi_file_type(md.file_type()),
@@ -1401,7 +1403,7 @@ impl WasiFs {
.ok_or(__WASI_EINVAL)?
.to_string_lossy()
.to_string();
if let Some(p) = parent.clone() {
if let Some(p) = *parent {
match &mut self.inodes[p].kind {
Kind::Dir { entries, .. } | Kind::Root { entries } => {
self.fd_map.remove(&fd).unwrap();
@@ -1478,8 +1480,9 @@ pub struct WasiState {
impl WasiState {
/// Create a [`WasiStateBuilder`] to construct a validated instance of
/// [`WasiState`].
pub fn new(program_name: &str) -> WasiStateBuilder {
create_wasi_state(program_name)
#[allow(clippy::new_ret_no_self)]
pub fn new(program_name: impl AsRef<str>) -> WasiStateBuilder {
create_wasi_state(program_name.as_ref())
}
/// Turn the WasiState into bytes

View File

@@ -22,6 +22,7 @@ pub fn fd_filestat_get(
let new_buf: WasmPtr<types::__wasi_filestat_t> = unsafe { std::mem::transmute(buf) };
// Copy the data including the extra data
#[allow(clippy::clone_on_copy)]
let new_filestat_setup: types::__wasi_filestat_t =
wasi_try!(new_buf.deref(memory)).get().clone();
@@ -71,6 +72,7 @@ pub fn path_filestat_get(
let memory = env.memory();
let new_buf: WasmPtr<types::__wasi_filestat_t> = unsafe { std::mem::transmute(buf) };
#[allow(clippy::clone_on_copy)]
let new_filestat_setup: types::__wasi_filestat_t =
wasi_try!(new_buf.deref(memory)).get().clone();

View File

@@ -1,4 +1,5 @@
#![allow(unused, clippy::too_many_arguments)]
#![allow(unused, clippy::too_many_arguments, clippy::cognitive_complexity)]
pub mod types;
#[cfg(any(
target_os = "freebsd",
@@ -596,14 +597,11 @@ pub fn fd_filestat_set_times(
};
inode.stat.st_atim = time_to_set;
// TODO: set it for more than just files
match &mut inode.kind {
Kind::File { handle, .. } => {
if let Kind::File { handle, .. } = &mut inode.kind {
if let Some(handle) = handle {
handle.set_last_accessed(time_to_set);
}
}
_ => {}
}
}
if fst_flags & __WASI_FILESTAT_SET_MTIM != 0 || fst_flags & __WASI_FILESTAT_SET_MTIM_NOW != 0 {
@@ -614,14 +612,11 @@ pub fn fd_filestat_set_times(
};
inode.stat.st_mtim = time_to_set;
// TODO: set it for more than just files
match &mut inode.kind {
Kind::File { handle, .. } => {
if let Kind::File { handle, .. } = &mut inode.kind {
if let Some(handle) = handle {
handle.set_last_modified(time_to_set);
}
}
_ => {}
}
}
__WASI_ESUCCESS
@@ -688,8 +683,7 @@ pub fn fd_pread(
h.seek(std::io::SeekFrom::Start(offset as u64)).ok(),
__WASI_EIO
);
let bytes_read = wasi_try!(read_bytes(h, memory, iov_cells));
bytes_read
wasi_try!(read_bytes(h, memory, iov_cells))
} else {
return __WASI_EINVAL;
}
@@ -836,7 +830,7 @@ pub fn fd_pwrite(
let inode = &mut state.fs.inodes[fd_entry.inode];
let bytes_written = match &mut inode.kind {
match &mut inode.kind {
Kind::File { handle, .. } => {
if let Some(handle) = handle {
handle.seek(std::io::SeekFrom::Start(offset as u64));
@@ -855,9 +849,7 @@ pub fn fd_pwrite(
memory,
iovs_arr_cell
)),
};
bytes_written
}
}
};
@@ -1534,14 +1526,11 @@ pub fn path_filestat_set_times(
};
inode.stat.st_atim = time_to_set;
// TODO: set it for more than just files
match &mut inode.kind {
Kind::File { handle, .. } => {
if let Kind::File { handle, .. } = &mut inode.kind {
if let Some(handle) = handle {
handle.set_last_accessed(time_to_set);
}
}
_ => {}
}
}
if fst_flags & __WASI_FILESTAT_SET_MTIM != 0 || fst_flags & __WASI_FILESTAT_SET_MTIM_NOW != 0 {
let time_to_set = if fst_flags & __WASI_FILESTAT_SET_MTIM != 0 {
@@ -1551,14 +1540,11 @@ pub fn path_filestat_set_times(
};
inode.stat.st_mtim = time_to_set;
// TODO: set it for more than just files
match &mut inode.kind {
Kind::File { handle, .. } => {
if let Kind::File { handle, .. } = &mut inode.kind {
if let Some(handle) = handle {
handle.set_last_modified(time_to_set);
}
}
_ => {}
}
}
__WASI_ESUCCESS
@@ -1734,11 +1720,9 @@ pub fn path_open(
if o_flags & __WASI_O_DIRECTORY != 0 {
return __WASI_ENOTDIR;
}
if o_flags & __WASI_O_EXCL != 0 {
if path.exists() {
if o_flags & __WASI_O_EXCL != 0 && path.exists() {
return __WASI_EEXIST;
}
}
let mut open_options = std::fs::OpenOptions::new();
let write_permission = adjusted_rights & __WASI_RIGHT_FD_WRITE != 0;
// append, truncate, and create all require the permission to write
@@ -1780,12 +1764,10 @@ pub fn path_open(
Kind::Buffer { .. } => unimplemented!("wasi::path_open for Buffer type files"),
Kind::Dir { .. } | Kind::Root { .. } => {
// TODO: adjust these to be correct
if o_flags & __WASI_O_EXCL != 0 {
if path_arg.exists() {
if o_flags & __WASI_O_EXCL != 0 && path_arg.exists() {
return __WASI_EEXIST;
}
}
}
Kind::Symlink {
base_po_dir,
path_to_symlink,
@@ -1971,12 +1953,10 @@ pub fn path_remove_directory(
let host_path_to_remove = match &state.fs.inodes[inode].kind {
Kind::Dir { entries, path, .. } => {
if !entries.is_empty() {
if !entries.is_empty()
|| wasi_try!(std::fs::read_dir(path).ok(), __WASI_EIO).count() != 0
{
return __WASI_ENOTEMPTY;
} else {
if wasi_try!(std::fs::read_dir(path).ok(), __WASI_EIO).count() != 0 {
return __WASI_ENOTEMPTY;
}
}
path.clone()
}

View File

@@ -290,6 +290,7 @@ impl PrestatEnum {
}
impl __wasi_prestat_t {
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn tagged(&self) -> Option<PrestatEnum> {
match self.pr_type {
__WASI_PREOPENTYPE_DIR => Some(PrestatEnum::Dir {

View File

@@ -1,6 +1,6 @@
[package]
name = "wasm-common"
version = "0.16.2"
version = "1.0.0-alpha.1"
description = "WebAssembly Common Types"
license = "MIT OR (Apache-2.0 WITH LLVM-exception)"
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]

View File

@@ -138,8 +138,8 @@ impl Features {
}
impl Default for Features {
fn default() -> Features {
Features::new()
fn default() -> Self {
Self::new()
}
}
@@ -156,7 +156,7 @@ mod test_features {
reference_types: false,
simd: false,
bulk_memory: false,
multi_value: false,
multi_value: true,
}
);
}

View File

@@ -6,7 +6,6 @@
#![deny(missing_docs, unused_extern_crates)]
#![warn(unused_import_braces)]
#![cfg_attr(feature = "std", deny(unstable_features))]
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
#![cfg_attr(

View File

@@ -30,12 +30,16 @@ pub trait NativeWasmType {
#[doc(hidden)]
fn into_abi(self) -> Self::Abi;
/// Convert self to i128 binary representation.
fn to_binary(self) -> i128;
/// Convert to self from i128 binary representation.
fn from_binary(binary: i128) -> Self;
}
macro_rules! wasm_native_type {
($type:ty => $native_type:expr) => {
impl NativeWasmType for $type {
const WASM_TYPE: Type = $native_type;
impl NativeWasmType for i32 {
const WASM_TYPE: Type = Type::I32;
type Abi = Self;
#[inline]
@@ -47,14 +51,81 @@ macro_rules! wasm_native_type {
fn into_abi(self) -> Self::Abi {
self
}
}
};
fn to_binary(self) -> i128 {
self as _
}
wasm_native_type!(i32 => Type::I32);
wasm_native_type!(i64 => Type::I64);
wasm_native_type!(f32 => Type::F32);
wasm_native_type!(f64 => Type::F64);
fn from_binary(bits: i128) -> Self {
bits as _
}
}
impl NativeWasmType for i64 {
const WASM_TYPE: Type = Type::I64;
type Abi = Self;
#[inline]
fn from_abi(abi: Self::Abi) -> Self {
abi
}
#[inline]
fn into_abi(self) -> Self::Abi {
self
}
fn to_binary(self) -> i128 {
self as _
}
fn from_binary(bits: i128) -> Self {
bits as _
}
}
impl NativeWasmType for f32 {
const WASM_TYPE: Type = Type::F32;
type Abi = Self;
#[inline]
fn from_abi(abi: Self::Abi) -> Self {
abi
}
#[inline]
fn into_abi(self) -> Self::Abi {
self
}
fn to_binary(self) -> i128 {
self.to_bits() as _
}
fn from_binary(bits: i128) -> Self {
Self::from_bits(bits as _)
}
}
impl NativeWasmType for f64 {
const WASM_TYPE: Type = Type::F64;
type Abi = Self;
#[inline]
fn from_abi(abi: Self::Abi) -> Self {
abi
}
#[inline]
fn into_abi(self) -> Self::Abi {
self
}
fn to_binary(self) -> i128 {
self.to_bits() as _
}
fn from_binary(bits: i128) -> Self {
Self::from_bits(bits as _)
}
}
#[cfg(test)]
mod test_native_type {
@@ -87,6 +158,7 @@ where
macro_rules! wasm_extern_type {
($type:ty => $native_type:ty) => {
#[allow(clippy::use_self)]
unsafe impl WasmExternType for $type {
type Native = $native_type;
@@ -147,18 +219,15 @@ where
{
}
macro_rules! convert_value_impl {
($t:ty) => {
unsafe impl ValueType for $t {}
};
( $($t:ty),* ) => {
macro_rules! impl_value_type_for {
( $($type:ty),* ) => {
$(
convert_value_impl!($t);
unsafe impl ValueType for $type {}
)*
};
}
convert_value_impl!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64);
impl_value_type_for!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64);
/// Represents a list of WebAssembly values.
pub trait WasmTypeList {
@@ -166,7 +235,7 @@ pub trait WasmTypeList {
type CStruct;
/// Array of return values.
type Array: AsMut<[u64]>;
type Array: AsMut<[i128]>;
/// Construct `Self` based on an array of returned values.
fn from_array(array: Self::Array) -> Self;
@@ -195,6 +264,7 @@ where
{
/// The error type for this trait.
type Error: Send + 'static;
/// Get returns or error result.
fn report(self) -> Result<Rets, Self::Error>;
}
@@ -204,7 +274,8 @@ where
Rets: WasmTypeList,
{
type Error = Infallible;
fn report(self) -> Result<Rets, Infallible> {
fn report(self) -> Result<Self, Infallible> {
Ok(self)
}
}
@@ -215,7 +286,8 @@ where
E: Send + 'static,
{
type Error = E;
fn report(self) -> Result<Rets, E> {
fn report(self) -> Self {
self
}
}
@@ -299,7 +371,7 @@ where
impl WasmTypeList for Infallible {
type CStruct = Self;
type Array = [u64; 0];
type Array = [i128; 0];
fn from_array(_: Self::Array) -> Self {
unreachable!()
@@ -351,20 +423,19 @@ macro_rules! impl_traits {
{
type CStruct = $struct_name<$( $x ),*>;
type Array = [u64; count_idents!( $( $x ),* )];
type Array = [i128; count_idents!( $( $x ),* )];
fn from_array(_array: Self::Array) -> Self {
unimplemented!("from array");
// #[allow(non_snake_case)]
// let [ $( $x ),* ] = array;
fn from_array(array: Self::Array) -> Self {
#[allow(non_snake_case)]
let [ $( $x ),* ] = array;
// ( $( WasmExternType::from_native(NativeWasmType::from_binary($x)) ),* )
( $( WasmExternType::from_native(NativeWasmType::from_binary($x)) ),* )
}
fn into_array(self) -> Self::Array {
unimplemented!("into array");
// let ( $( $x ),* ) = self;
// [ $( WasmExternType::to_native($x).to_binary() ),* ]
#[allow(non_snake_case)]
let ( $( $x ),* ) = self;
[ $( WasmExternType::to_native($x).to_binary() ),* ]
}
fn empty_array() -> Self::Array {
@@ -409,7 +480,7 @@ macro_rules! impl_traits {
// println!("WRAP");
// println!("Struct {:?}", (($( $x ),*) as WasmTypeList).into_c_struct());
// $( println!("X: {:?}", $x); )*
let f: &FN = unsafe { std::mem::transmute(&()) };
let f: &FN = unsafe { &*(&() as *const () as *const FN) };
f( $( WasmExternType::from_native($x) ),* ).into_c_struct()
}
wrap::<$( $x, )* Rets, Self> as *const FunctionBody
@@ -486,7 +557,7 @@ macro_rules! impl_traits {
T: Sized,
FN: Fn(&mut T, $( $x ),* ) -> Rets + 'static
{
let f: &FN = unsafe { std::mem::transmute(&()) };
let f: &FN = unsafe { &*(&() as *const () as *const FN) };
f(ctx, $( WasmExternType::from_native($x) ),* ).into_c_struct()
}
wrap::<$( $x, )* Rets, Self, T> as *const FunctionBody
@@ -520,11 +591,11 @@ impl_traits!([C] S12, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12);
impl_traits!([C] S13, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13);
impl_traits!([C] S14, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14);
impl_traits!([C] S15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15);
// impl_traits!([C] S16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16);
// impl_traits!([C] S17, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17);
// impl_traits!([C] S18, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18);
// impl_traits!([C] S19, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19);
// impl_traits!([C] S20, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20);
impl_traits!([C] S16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16);
impl_traits!([C] S17, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17);
impl_traits!([C] S18, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18);
impl_traits!([C] S19, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19);
impl_traits!([C] S20, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20);
// impl_traits!([C] S21, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21);
// impl_traits!([C] S22, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22);
// impl_traits!([C] S23, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23);
@@ -593,6 +664,7 @@ mod test_wasm_type_list {
mod test_func {
use super::*;
use crate::types::Type;
use std::ptr;
// WasmTypeList
fn func() {}
@@ -615,64 +687,44 @@ mod test_func {
#[test]
fn test_function_types() {
assert_eq!(Function::new(func).ty(), FunctionType::new(vec![], vec![]));
assert_eq!(Func::new(func).ty(), FunctionType::new(vec![], vec![]));
assert_eq!(
Function::new(func__i32).ty(),
Func::new(func__i32).ty(),
FunctionType::new(vec![], vec![Type::I32])
);
assert_eq!(
Function::new(func_i32).ty(),
Func::new(func_i32).ty(),
FunctionType::new(vec![Type::I32], vec![])
);
assert_eq!(
Function::new(func_i32__i32).ty(),
Func::new(func_i32__i32).ty(),
FunctionType::new(vec![Type::I32], vec![Type::I32])
);
assert_eq!(
Function::new(func_i32_i32__i32).ty(),
Func::new(func_i32_i32__i32).ty(),
FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32])
);
assert_eq!(
Function::new(func_i32_i32__i32_i32).ty(),
Func::new(func_i32_i32__i32_i32).ty(),
FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32, Type::I32])
);
assert_eq!(
Function::new(func_f32_i32__i32_f32).ty(),
Func::new(func_f32_i32__i32_f32).ty(),
FunctionType::new(vec![Type::F32, Type::I32], vec![Type::I32, Type::F32])
);
}
#[test]
fn test_function_pointer() {
let f = Function::new(func_i32__i32);
let function = unsafe {
std::mem::transmute::<*const FunctionBody, fn(i32, i32, i32) -> i32>(f.address)
};
assert_eq!(function(1, 2, 3), 6);
}
#[test]
fn test_function_env_pointer() {
fn func_i32__i32_env(env: &mut Env, a: i32) -> i32 {
let result = env.num * a;
env.num = 10;
return result;
}
struct Env {
pub num: i32,
};
let mut my_env = Env { num: 2 };
let f = Function::new_env(&mut my_env, func_i32__i32_env);
let function = unsafe {
std::mem::transmute::<*const FunctionBody, fn(&mut Env, i32) -> i32>(f.address)
};
assert_eq!(function(&mut my_env, 3), 6);
assert_eq!(my_env.num, 10);
let f = Func::new(func_i32__i32);
let function =
unsafe { std::mem::transmute::<*const FunctionBody, fn(usize, i32) -> i32>(f.address) };
assert_eq!(function(0, 3), 6);
}
#[test]
fn test_function_call() {
let f = Function::new(func_i32__i32);
let f = Func::new(func_i32__i32);
let x = |args: <(i32, i32) as WasmTypeList>::Array,
rets: &mut <(i32, i32) as WasmTypeList>::Array| {
let result = func_i32_i32__i32_i32(args[0] as _, args[1] as _);

View File

@@ -100,9 +100,9 @@ impl AnyRef {
/// Returns true if the two `AnyRef<T>`'s point to the same value (not just
/// values that compare as equal).
pub fn ptr_eq(&self, other: &AnyRef) -> bool {
pub fn ptr_eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Null, AnyRef::Null) => true,
(Self::Null, Self::Null) => true,
(Self::Ref(InternalRef(ref a)), Self::Ref(InternalRef(ref b))) => a.ptr_eq(b.as_ref()),
(Self::Other(OtherRef(ref a)), Self::Other(OtherRef(ref b))) => Rc::ptr_eq(a, b),
_ => false,

View File

@@ -32,7 +32,7 @@ pub enum Type {
impl Type {
/// Returns true if `Type` matches any of the numeric types. (e.g. `I32`,
/// `I64`, `F32`, `F64`, `V128`).
pub fn is_num(&self) -> bool {
pub fn is_num(self) -> bool {
match self {
Self::I32 | Self::I64 | Self::F32 | Self::F64 | Self::V128 => true,
_ => false,
@@ -40,7 +40,7 @@ impl Type {
}
/// Returns true if `Type` matches either of the reference types.
pub fn is_ref(&self) -> bool {
pub fn is_ref(self) -> bool {
match self {
Self::AnyRef | Self::FuncRef => true,
_ => false,
@@ -109,7 +109,7 @@ pub enum ExternType {
Memory(MemoryType),
}
fn is_global_compatible(exported: &GlobalType, imported: &GlobalType) -> bool {
fn is_global_compatible(exported: GlobalType, imported: GlobalType) -> bool {
let GlobalType {
ty: exported_ty,
mutability: exported_mutability,
@@ -118,6 +118,7 @@ fn is_global_compatible(exported: &GlobalType, imported: &GlobalType) -> bool {
ty: imported_ty,
mutability: imported_mutability,
} = imported;
exported_ty == imported_ty && imported_mutability == exported_mutability
}
@@ -201,7 +202,7 @@ impl ExternType {
pub fn is_compatible_with(&self, other: &Self) -> bool {
match (self, other) {
(Self::Function(a), Self::Function(b)) => a == b,
(Self::Global(a), Self::Global(b)) => is_global_compatible(a, b),
(Self::Global(a), Self::Global(b)) => is_global_compatible(*a, *b),
(Self::Table(a), Self::Table(b)) => is_table_compatible(a, b),
(Self::Memory(a), Self::Memory(b)) => is_memory_compatible(a, b),
// The rest of possibilities, are not compatible
@@ -289,25 +290,26 @@ pub enum Mutability {
impl Mutability {
/// Returns a boolean indicating if the enum is set to mutable.
pub fn is_mutable(&self) -> bool {
(*self).into()
pub fn is_mutable(self) -> bool {
self.into()
}
}
impl From<bool> for Mutability {
fn from(val: bool) -> Self {
match val {
false => Self::Const,
true => Self::Var,
fn from(value: bool) -> Self {
if value {
Self::Var
} else {
Self::Const
}
}
}
impl From<Mutability> for bool {
fn from(val: Mutability) -> Self {
match val {
Mutability::Const => false,
fn from(value: Mutability) -> Self {
match value {
Mutability::Var => true,
Mutability::Const => false,
}
}
}
@@ -550,7 +552,7 @@ impl<T> ExportType<T> {
/// Creates a new export which is exported with the given `name` and has the
/// given `ty`.
pub fn new(name: &str, ty: T) -> Self {
ExportType {
Self {
name: name.to_string(),
ty,
}

View File

@@ -98,7 +98,7 @@ impl<T> Value<T> {
/// `p` must be:
/// - Properly aligned to the specified `ty`'s Rust equivalent
/// - Non-null and pointing to valid memory
pub unsafe fn read_value_from(p: *const i128, ty: Type) -> Value<T> {
pub unsafe fn read_value_from(p: *const i128, ty: Type) -> Self {
match ty {
Type::I32 => Self::I32(ptr::read(p as *const i32)),
Type::I64 => Self::I64(ptr::read(p as *const i64)),

View File

@@ -1,6 +1,6 @@
# A script to update the version of all the crates at the same time
PREVIOUS_VERSION='0.16.2'
NEXT_VERSION='0.17.0'
PREVIOUS_VERSION='1.0.0-alpha.1'
NEXT_VERSION='1.0.0-alpha.2'
# quick hack
fd Cargo.toml --exec sed -i '' "s/version = \"$PREVIOUS_VERSION\"/version = \"$NEXT_VERSION\"/"

View File

@@ -26,7 +26,14 @@ impl Inspect {
let (store, _engine_type, _compiler_type) = self.store.get_store()?;
let module_contents = std::fs::read(&self.path)?;
let module = Module::new(&store, &module_contents)?;
println!("Type: {}", if module.from_wat { "wat" } else { "wasm" });
println!(
"Type: {}",
if !is_wasm(&module_contents) {
"wat"
} else {
"wasm"
}
);
println!("Size: {}", ByteSize(module_contents.len() as _));
println!("Imports:");
println!(" Functions:");

View File

@@ -1,5 +1,5 @@
use crate::common::get_cache_dir;
use crate::store::StoreOptions;
use crate::store::{CompilerType, EngineType, StoreOptions};
use crate::suggestions::suggest_function_exports;
use crate::warning;
use anyhow::{anyhow, Context, Result};
@@ -68,23 +68,27 @@ pub struct Run {
}
impl Run {
#[cfg(feature = "cache")]
/// Get the Compiler Filesystem cache
fn get_cache(&self, engine_name: String, compiler_name: String) -> Result<FileSystemCache> {
let mut cache_dir_root = get_cache_dir();
cache_dir_root.push(engine_name);
cache_dir_root.push(compiler_name);
Ok(FileSystemCache::new(cache_dir_root)?)
}
/// Execute the run command
pub fn execute(&self) -> Result<()> {
self.inner_execute()
.context(format!("failed to run `{}`", self.path.display()))
self.inner_execute().with_context(|| {
let compilers = CompilerType::enabled()
.iter()
.map(|c| c.to_string())
.collect::<Vec<String>>();
format!(
"failed to run `{}`{}",
self.path.display(),
if compilers.len() > 0 {
""
} else {
" (no compilers enabled)"
}
)
})
}
fn inner_execute(&self) -> Result<()> {
let module = self
.get_module()
.with_context(|| "module instantiation failed")?;
let module = self.get_module()?;
// Do we want to invoke a function?
if let Some(ref invoke) = self.invoke {
let imports = imports! {};
@@ -105,7 +109,7 @@ impl Run {
#[cfg(feature = "wasi")]
{
let wasi_version = Wasi::get_version(&module);
if let Some(version) = wasi_version {
if wasi_version.is_some() {
let program_name = self
.command_name
.clone()
@@ -117,7 +121,7 @@ impl Run {
.unwrap_or_default();
return self
.wasi
.execute(module, version, program_name, self.args.clone())
.execute(module, program_name, self.args.clone())
.with_context(|| "WASI execution failed");
}
}
@@ -154,14 +158,41 @@ impl Run {
}
}
let (store, engine_type, compiler_type) = self.store.get_store()?;
#[cfg(feature = "cache")]
let module_result: Result<Module> = if !self.disable_cache && contents.len() > 0x1000 {
self.get_module_from_cache(&store, &contents, &engine_type, &compiler_type)
} else {
Module::new(&store, &contents).map_err(|e| e.into())
};
#[cfg(not(feature = "cache"))]
let module_result = Module::new(&store, &contents);
let mut module = module_result.with_context(|| {
format!(
"module instantiation failed (engine: {}, compiler: {})",
engine_type.to_string(),
compiler_type.to_string()
)
})?;
// We set the name outside the cache, to make sure we dont cache the name
module.set_name(&self.path.file_name().unwrap_or_default().to_string_lossy());
Ok(module)
}
#[cfg(feature = "cache")]
fn get_module_from_cache(
&self,
store: &Store,
contents: &[u8],
engine_type: &EngineType,
compiler_type: &CompilerType,
) -> Result<Module> {
// We try to get it from cache, in case caching is enabled
// and the file length is greater than 4KB.
// For files smaller than 4KB caching is not worth,
// as it takes space and the speedup is minimal.
let mut module =
if cfg!(feature = "cache") && !self.disable_cache && contents.len() > 0x1000 {
let mut cache =
self.get_cache(engine_type.to_string(), compiler_type.to_string())?;
let mut cache = self.get_cache(engine_type.to_string(), compiler_type.to_string())?;
// Try to get the hash from the provided `--cache-key`, otherwise
// generate one from the provided file `.wasm` contents.
let hash = self
@@ -170,7 +201,7 @@ impl Run {
.and_then(|key| WasmHash::from_str(&key).ok())
.unwrap_or_else(|| WasmHash::generate(&contents));
match unsafe { cache.load(&store, hash) } {
Ok(module) => module,
Ok(module) => Ok(module),
Err(e) => {
match e {
DeserializeError::Io(_) => {
@@ -182,18 +213,20 @@ impl Run {
}
let module = Module::new(&store, &contents)?;
// Store the compiled Module in cache
cache.store(hash, module.clone())?;
module
}
}
} else {
Module::new(&store, &contents)?
};
// We set the name outside the cache, to make sure we dont cache the name
module.set_name(&self.path.file_name().unwrap_or_default().to_string_lossy());
cache.store(hash, &module)?;
Ok(module)
}
}
}
#[cfg(feature = "cache")]
/// Get the Compiler Filesystem cache
fn get_cache(&self, engine_name: String, compiler_name: String) -> Result<FileSystemCache> {
let mut cache_dir_root = get_cache_dir();
cache_dir_root.push(engine_name);
cache_dir_root.push(compiler_name);
Ok(FileSystemCache::new(cache_dir_root)?)
}
fn try_find_function(
&self,

View File

@@ -1,10 +1,8 @@
use crate::utils::{parse_envvar, parse_mapdir};
use anyhow::{Context, Result};
use std::path::PathBuf;
use wasmer::{Function, Instance, Memory, Module};
use wasmer_wasi::{
generate_import_object_from_env, get_wasi_version, WasiEnv, WasiState, WasiVersion,
};
use wasmer::{Instance, Module};
use wasmer_wasi::{get_wasi_version, WasiState, WasiVersion};
use structopt::StructOpt;
@@ -12,16 +10,16 @@ use structopt::StructOpt;
/// WASI Options
pub struct Wasi {
/// WASI pre-opened directory
#[structopt(long = "dir", multiple = true, group = "wasi")]
#[structopt(long = "dir", name = "DIR", multiple = true, group = "wasi")]
pre_opened_directories: Vec<PathBuf>,
/// Map a host directory to a different location for the wasm module
#[structopt(long = "mapdir", multiple = true)]
mapped_dirs: Vec<String>,
#[structopt(long = "mapdir", name = "GUEST_DIR:HOST_DIR", multiple = true, parse(try_from_str = parse_mapdir))]
mapped_dirs: Vec<(String, PathBuf)>,
/// Pass custom environment variables
#[structopt(long = "env", multiple = true)]
env_vars: Vec<String>,
#[structopt(long = "env", name = "KEY=VALUE", multiple = true, parse(try_from_str = parse_envvar))]
env_vars: Vec<(String, String)>,
/// Enable experimental IO devices
#[cfg(feature = "experimental-io-devices")]
@@ -30,44 +28,6 @@ pub struct Wasi {
}
impl Wasi {
fn get_mapped_dirs(&self) -> Result<Vec<(String, PathBuf)>> {
let mut md = vec![];
for entry in self.mapped_dirs.iter() {
if let [alias, real_dir] = entry.split(':').collect::<Vec<&str>>()[..] {
let pb = PathBuf::from(&real_dir);
if let Ok(pb_metadata) = pb.metadata() {
if !pb_metadata.is_dir() {
bail!("\"{}\" exists, but it is not a directory", &real_dir);
}
} else {
bail!("Directory \"{}\" does not exist", &real_dir);
}
md.push((alias.to_string(), pb));
continue;
}
bail!(
"Directory mappings must consist of two paths separate by a colon. Found {}",
&entry
);
}
Ok(md)
}
fn get_env_vars(&self) -> Result<Vec<(&str, &str)>> {
let mut env = vec![];
for entry in self.env_vars.iter() {
if let [env_var, value] = entry.split('=').collect::<Vec<&str>>()[..] {
env.push((env_var, value));
} else {
bail!(
"Env vars must be of the form <var_name>=<value>. Found {}",
&entry
);
}
}
Ok(env)
}
/// Gets the WASI version (if any) for the provided module
pub fn get_version(module: &Module) -> Option<WasiVersion> {
// Get the wasi version on strict mode, so no other imports are
@@ -76,25 +36,15 @@ impl Wasi {
}
/// Helper function for executing Wasi from the `Run` command.
pub fn execute(
&self,
module: Module,
wasi_version: WasiVersion,
program_name: String,
args: Vec<String>,
) -> Result<()> {
let mut wasi_state_builder = WasiState::new(&program_name);
pub fn execute(&self, module: Module, program_name: String, args: Vec<String>) -> Result<()> {
let args = args.iter().cloned().map(|arg| arg.into_bytes());
let env_vars = self.get_env_vars()?;
let preopened_files = self.pre_opened_directories.clone();
let mapped_dirs = self.get_mapped_dirs()?;
let mut wasi_state_builder = WasiState::new(program_name);
wasi_state_builder
.args(args)
.envs(env_vars)
.preopen_dirs(preopened_files)?
.map_dirs(mapped_dirs)?;
.envs(self.env_vars.clone())
.preopen_dirs(self.pre_opened_directories.clone())?
.map_dirs(self.mapped_dirs.clone())?;
#[cfg(feature = "experimental-io-devices")]
{
@@ -104,18 +54,13 @@ impl Wasi {
}
}
let wasi_state = wasi_state_builder.build()?;
let mut wasi_env = WasiEnv::new(wasi_state);
let import_object =
generate_import_object_from_env(module.store(), &mut wasi_env, wasi_version);
let mut wasi_env = wasi_state_builder.finalize()?;
let import_object = wasi_env.import_object(&module)?;
let instance = Instance::new(&module, &import_object)?;
let memory: &Memory = instance.exports.get("memory")?;
wasi_env.set_memory(memory);
let start: &Function = instance.exports.get("_start")?;
wasi_env.set_memory(instance.exports.get_memory("memory")?);
let start = instance.exports.get_function("_start")?;
start
.call(&[])
.with_context(|| "failed to run WASI `_start` function")?;

View File

@@ -9,6 +9,7 @@ use std::string::ToString;
use std::sync::Arc;
use structopt::StructOpt;
use wasmer::*;
#[cfg(feature = "compiler")]
use wasmer_compiler::CompilerConfig;
#[derive(Debug, Clone, StructOpt)]
@@ -61,6 +62,20 @@ pub enum CompilerType {
Headless,
}
impl CompilerType {
/// Return all enabled compilers
pub fn enabled() -> Vec<CompilerType> {
vec![
#[cfg(feature = "singlepass")]
Self::Singlepass,
#[cfg(feature = "cranelift")]
Self::Cranelift,
#[cfg(feature = "llvm")]
Self::LLVM,
]
}
}
impl ToString for CompilerType {
fn to_string(&self) -> String {
match self {
@@ -104,13 +119,13 @@ impl StoreOptions {
// Auto mode, we choose the best compiler for that platform
cfg_if::cfg_if! {
if #[cfg(all(feature = "cranelift", target_arch = "x86_64"))] {
return Ok(CompilerType::Cranelift);
Ok(CompilerType::Cranelift)
}
else if #[cfg(all(feature = "singlepass", target_arch = "x86_64"))] {
return Ok(CompilerType::Singlepass);
Ok(CompilerType::Singlepass)
}
else if #[cfg(feature = "llvm")] {
return Ok(CompilerType::LLVM);
Ok(CompilerType::LLVM)
} else {
bail!("There are no available compilers for your architecture");
}
@@ -303,7 +318,7 @@ impl StoreOptions {
engine.to_string()
),
};
return Ok((engine, engine_type));
Ok((engine, engine_type))
}
}
@@ -363,7 +378,7 @@ impl StoreOptions {
engine.to_string()
),
};
return Ok((engine, engine_type));
Ok((engine, engine_type))
}
/// Get the store (headless engine)

View File

@@ -1,5 +1,7 @@
//! Utility functions for the WebAssembly module
use anyhow::{bail, Result};
use std::env;
use std::path::PathBuf;
/// Whether or not Wasmer should print with color
pub fn wasmer_should_print_color() -> bool {
@@ -8,3 +10,34 @@ pub fn wasmer_should_print_color() -> bool {
.and_then(|inner| inner.parse::<bool>().ok())
.unwrap_or_else(|| atty::is(atty::Stream::Stdout))
}
/// Parses a mapdir from a string
pub fn parse_mapdir(entry: &str) -> Result<(String, PathBuf)> {
if let [alias, real_dir] = entry.split(':').collect::<Vec<&str>>()[..] {
let pb = PathBuf::from(&real_dir);
if let Ok(pb_metadata) = pb.metadata() {
if !pb_metadata.is_dir() {
bail!("\"{}\" exists, but it is not a directory", &real_dir);
}
} else {
bail!("Directory \"{}\" does not exist", &real_dir);
}
return Ok((alias.to_string(), pb));
}
bail!(
"Directory mappings must consist of two paths separate by a colon. Found {}",
&entry
)
}
/// Parses a mapdir from an env var
pub fn parse_envvar(entry: &str) -> Result<(String, String)> {
if let [env_var, value] = entry.split('=').collect::<Vec<&str>>()[..] {
return Ok((env_var.to_string(), value.to_string()));
} else {
bail!(
"Env vars must be of the form <var_name>=<value>. Found {}",
&entry
);
}
}

View File

@@ -1,6 +1,6 @@
[Setup]
AppName=Wasmer
AppVersion=0.16.2
AppVersion=1.0.0-alpha.1
DefaultDirName={pf}\Wasmer
DefaultGroupName=Wasmer
Compression=lzma2

View File

@@ -0,0 +1,41 @@
use crate::utils::get_store;
use anyhow::Result;
use wasmer::*;
#[test]
fn native_function_works_for_wasm() -> Result<()> {
let store = get_store();
let wat = r#"(module
(func $multiply (import "env" "multiply") (param i32 i32) (result i32))
(func (export "add") (param i32 i32) (result i32)
(i32.add (local.get 0)
(local.get 1)))
(func (export "double_then_add") (param i32 i32) (result i32)
(i32.add (call $multiply (local.get 0) (i32.const 2))
(call $multiply (local.get 1) (i32.const 2))))
)"#;
let module = Module::new(&store, wat).unwrap();
let import_object = imports! {
"env" => {
"multiply" => Function::new(&store, |a: i32, b: i32| a * b),
},
};
let instance = Instance::new(&module, &import_object)?;
let f: NativeFunc<(i32, i32), i32> = instance.exports.get_native_function("add")?;
let result = f.call(4, 6)?;
assert_eq!(result, 10);
let dyn_f: &Function = instance.exports.get("double_then_add")?;
let dyn_result = dyn_f.call(&[Val::I32(4), Val::I32(6)])?;
assert_eq!(dyn_result[0], Val::I32(20));
let f: NativeFunc<(i32, i32), i32> = dyn_f.native().unwrap();
let result = f.call(4, 6)?;
assert_eq!(result, 20);
Ok(())
}

View File

@@ -2,10 +2,10 @@
//! This tests checks that the provided functions (both native and
//! dynamic ones) work properly.
wasmer_compilers! {
use wasmer::*;
use crate::utils::get_store;
use anyhow::Result;
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
use wasmer::*;
fn get_module(store: &Store) -> Result<Module> {
let wat = r#"
@@ -77,7 +77,6 @@ wasmer_compilers! {
Ok(())
}
#[test]
fn dynamic_function_with_env() -> Result<()> {
let store = get_store();
@@ -194,4 +193,3 @@ wasmer_compilers! {
assert_eq!(env.load(SeqCst), 4);
Ok(())
}
}

View File

@@ -1,66 +0,0 @@
#[macro_export]
macro_rules! wasmer_compilers {
{ $($code:item)* } => {
#[cfg(feature = "singlepass")]
#[cfg(test)]
mod singlepass {
use std::sync::Arc;
use wasmer::{Features, Store, Tunables};
use wasmer_engine_jit::JITEngine;
use test_utils::get_compiler_config_from_str;
#[allow(dead_code)]
fn get_store() -> Store {
let features = Features::default();
let try_nan_canonicalization = false;
let compiler_config =
get_compiler_config_from_str("singlepass", try_nan_canonicalization, features);
let tunables = Tunables::for_target(compiler_config.target().triple());
let store = Store::new(Arc::new(JITEngine::new(compiler_config, tunables)));
store
}
$($code)*
}
#[cfg(feature = "cranelift")]
#[cfg(test)]
mod cranelift {
use std::sync::Arc;
use wasmer::{Features, Store, Tunables};
use wasmer_engine_jit::JITEngine;
use test_utils::get_compiler_config_from_str;
#[allow(dead_code)]
fn get_store() -> Store {
let features = Features::default();
let try_nan_canonicalization = false;
let compiler_config =
get_compiler_config_from_str("cranelift", try_nan_canonicalization, features);
let tunables = Tunables::for_target(compiler_config.target().triple());
let store = Store::new(Arc::new(JITEngine::new(compiler_config, tunables)));
store
}
$($code)*
}
#[cfg(feature = "llvm")]
#[cfg(test)]
mod llvm {
use std::sync::Arc;
use wasmer::{Features, Store, Tunables};
use wasmer_engine_jit::JITEngine;
use test_utils::get_compiler_config_from_str;
#[allow(dead_code)]
fn get_store() -> Store {
let features = Features::default();
let try_nan_canonicalization = false;
let compiler_config =
get_compiler_config_from_str("llvm", try_nan_canonicalization, features);
let tunables = Tunables::for_target(compiler_config.target().triple());
let store = Store::new(Arc::new(JITEngine::new(compiler_config, tunables)));
store
}
$($code)*
}
};
}

View File

@@ -1,8 +1,12 @@
#![cfg(feature = "compiler")]
//! This test suite does all the tests that involve any compiler
//! implementation, such as: singlepass, cranelift or llvm depending
//! on what's available on the target.
#[macro_use]
mod macros;
mod functions;
mod imports;
mod wast;
mod multi_value_imports;
mod serialize;
mod traps;
mod utils;

View File

@@ -0,0 +1,207 @@
//! Testing the imports with different provided functions.
//! This tests checks that the provided functions (both native and
//! dynamic ones) work properly.
use crate::utils::get_store;
use wasmer::*;
macro_rules! mvr_test {
($test_name:ident, $( $result_type:ty ),* ) => {
mod $test_name {
use super::*;
fn get_module(store: &Store) -> anyhow::Result<wasmer::Module> {
let wat: String = r#"
(type $type (func (param i32) (result
"#.to_string() +
&stringify!( $( $result_type ),* ).replace(",", "").replace("(", "").replace(")", "") + &r#")))
(import "host" "callback_fn" (func $callback_fn (type $type)))
(func (export "test_call") (type $type)
get_local 0
call $callback_fn)
(func (export "test_call_indirect") (type $type)
(i32.const 1)
(call_indirect (type $type) (i32.const 0))
)
(table funcref
(elem
$callback_fn
)
)
"#.to_string();
Ok(wasmer::Module::new(&store, &wat)?)
}
fn callback_fn(n: i32) -> ( $( $result_type ),* ) {
( $( <$result_type>::expected_value(n) ),* )
}
#[test]
#[cfg_attr(any(feature = "test-cranelift", feature="test-singlepass"), ignore)]
fn native() -> anyhow::Result<()> {
let store = get_store();
let module = get_module(&store)?;
let instance = wasmer::Instance::new(
&module,
&wasmer::imports! {
"host" => {
"callback_fn" => wasmer::Function::new(&store, callback_fn)
}
}
)?;
let expected_value = vec![ $( <$result_type>::expected_val(1) ),* ].into_boxed_slice();
assert_eq!(instance.exports.get_function("test_call")?.call(&[wasmer::Val::I32(1)])?,
expected_value);
assert_eq!(instance.exports.get_function("test_call_indirect")?.call(&[wasmer::Val::I32(1)])?,
expected_value);
Ok(())
}
fn dynamic_callback_fn(values: &[wasmer::Value]) -> Result<Vec<wasmer::Val>, wasmer::RuntimeError> {
assert_eq!(values[0], wasmer::Value::I32(1));
Ok(vec![ $( <$result_type>::expected_val(1) ),* ])
}
#[test]
#[cfg_attr(feature="test-singlepass", ignore)]
fn dynamic() -> anyhow::Result<()> {
let store = get_store();
let module = get_module(&store)?;
let instance = wasmer::Instance::new(
&module,
&wasmer::imports! {
"host" => {
"callback_fn" => wasmer::Function::new_dynamic(&store, &wasmer::FunctionType::new(vec![wasmer::ValType::I32], vec![ $( <$result_type>::expected_valtype() ),* ]), dynamic_callback_fn)
}
}
)?;
let expected_value = vec![ $( <$result_type>::expected_val(1) ),* ].into_boxed_slice();
assert_eq!(instance.exports.get_function("test_call")?.call(&[wasmer::Val::I32(1)])?,
expected_value);
assert_eq!(instance.exports.get_function("test_call_indirect")?.call(&[wasmer::Val::I32(1)])?,
expected_value);
Ok(())
}
}
}
}
trait ExpectedExpr {
fn expected_value(n: i32) -> Self;
fn expected_val(n: i32) -> wasmer::Val;
fn expected_valtype() -> wasmer::ValType;
}
impl ExpectedExpr for i32 {
fn expected_value(n: i32) -> i32 {
n + 1
}
fn expected_val(n: i32) -> wasmer::Val {
wasmer::Val::I32(Self::expected_value(n))
}
fn expected_valtype() -> wasmer::ValType {
wasmer::ValType::I32
}
}
impl ExpectedExpr for i64 {
fn expected_value(n: i32) -> i64 {
n as i64 + 2i64
}
fn expected_val(n: i32) -> wasmer::Val {
wasmer::Val::I64(Self::expected_value(n))
}
fn expected_valtype() -> wasmer::ValType {
wasmer::ValType::I64
}
}
impl ExpectedExpr for f32 {
fn expected_value(n: i32) -> f32 {
n as f32 * 0.1
}
fn expected_val(n: i32) -> wasmer::Val {
wasmer::Val::F32(Self::expected_value(n))
}
fn expected_valtype() -> wasmer::ValType {
wasmer::ValType::F32
}
}
impl ExpectedExpr for f64 {
fn expected_value(n: i32) -> f64 {
n as f64 * 0.12
}
fn expected_val(n: i32) -> wasmer::Val {
wasmer::Val::F64(Self::expected_value(n))
}
fn expected_valtype() -> wasmer::ValType {
wasmer::ValType::F64
}
}
mvr_test!(test_mvr_i32_i32, i32, i32);
mvr_test!(test_mvr_i32_f32, i32, f32);
mvr_test!(test_mvr_f32_i32, f32, i32);
mvr_test!(test_mvr_f32_f32, f32, f32);
mvr_test!(test_mvr_i64_i32, i64, i32);
mvr_test!(test_mvr_i64_f32, i64, f32);
mvr_test!(test_mvr_f64_i32, f64, i32);
mvr_test!(test_mvr_f64_f32, f64, f32);
mvr_test!(test_mvr_i32_i64, i32, i64);
mvr_test!(test_mvr_f32_i64, f32, i64);
mvr_test!(test_mvr_i32_f64, i32, f64);
mvr_test!(test_mvr_f32_f64, f32, f64);
mvr_test!(test_mvr_i32_i32_i32, i32, i32, i32);
mvr_test!(test_mvr_i32_i32_f32, i32, i32, f32);
mvr_test!(test_mvr_i32_f32_i32, i32, f32, i32);
mvr_test!(test_mvr_i32_f32_f32, i32, f32, f32);
mvr_test!(test_mvr_f32_i32_i32, f32, i32, i32);
mvr_test!(test_mvr_f32_i32_f32, f32, i32, f32);
mvr_test!(test_mvr_f32_f32_i32, f32, f32, i32);
mvr_test!(test_mvr_f32_f32_f32, f32, f32, f32);
mvr_test!(test_mvr_i32_i32_i64, i32, i32, i64);
mvr_test!(test_mvr_i32_f32_i64, i32, f32, i64);
mvr_test!(test_mvr_f32_i32_i64, f32, i32, i64);
mvr_test!(test_mvr_f32_f32_i64, f32, f32, i64);
mvr_test!(test_mvr_i32_i32_f64, i32, i32, f64);
mvr_test!(test_mvr_i32_f32_f64, i32, f32, f64);
mvr_test!(test_mvr_f32_i32_f64, f32, i32, f64);
mvr_test!(test_mvr_f32_f32_f64, f32, f32, f64);
mvr_test!(test_mvr_i32_i64_i32, i32, i64, i32);
mvr_test!(test_mvr_i32_i64_f32, i32, i64, f32);
mvr_test!(test_mvr_f32_i64_i32, f32, i64, i32);
mvr_test!(test_mvr_f32_i64_f32, f32, i64, f32);
mvr_test!(test_mvr_i32_f64_i32, i32, f64, i32);
mvr_test!(test_mvr_i32_f64_f32, i32, f64, f32);
mvr_test!(test_mvr_f32_f64_i32, f32, f64, i32);
mvr_test!(test_mvr_f32_f64_f32, f32, f64, f32);
mvr_test!(test_mvr_i64_i32_i32, i64, i32, i32);
mvr_test!(test_mvr_i64_i32_f32, i64, i32, f32);
mvr_test!(test_mvr_i64_f32_i32, i64, f32, i32);
mvr_test!(test_mvr_i64_f32_f32, i64, f32, f32);
mvr_test!(test_mvr_f64_i32_i32, f64, i32, i32);
mvr_test!(test_mvr_f64_i32_f32, f64, i32, f32);
mvr_test!(test_mvr_f64_f32_i32, f64, f32, i32);
mvr_test!(test_mvr_f64_f32_f32, f64, f32, f32);
mvr_test!(test_mvr_i32_i32_i32_i32, i32, i32, i32, i32);
mvr_test!(test_mvr_i32_i32_i32_f32, i32, i32, i32, f32);
mvr_test!(test_mvr_i32_i32_f32_i32, i32, i32, f32, i32);
mvr_test!(test_mvr_i32_i32_f32_f32, i32, i32, f32, f32);
mvr_test!(test_mvr_i32_f32_i32_i32, i32, f32, i32, i32);
mvr_test!(test_mvr_i32_f32_i32_f32, i32, f32, i32, f32);
mvr_test!(test_mvr_i32_f32_f32_i32, i32, f32, f32, i32);
mvr_test!(test_mvr_i32_f32_f32_f32, i32, f32, f32, f32);
mvr_test!(test_mvr_f32_i32_i32_i32, f32, i32, i32, i32);
mvr_test!(test_mvr_f32_i32_i32_f32, f32, i32, i32, f32);
mvr_test!(test_mvr_f32_i32_f32_i32, f32, i32, f32, i32);
mvr_test!(test_mvr_f32_i32_f32_f32, f32, i32, f32, f32);
mvr_test!(test_mvr_f32_f32_i32_i32, f32, f32, i32, i32);
mvr_test!(test_mvr_f32_f32_i32_f32, f32, f32, i32, f32);
mvr_test!(test_mvr_f32_f32_f32_i32, f32, f32, f32, i32);
mvr_test!(test_mvr_f32_f32_f32_f32, f32, f32, f32, f32);
mvr_test!(test_mvr_i32_i32_i32_i32_i32, i32, i32, i32, i32, i32);

View File

@@ -0,0 +1,77 @@
use crate::utils::{get_headless_store, get_store};
use anyhow::Result;
use wasmer::*;
#[test]
fn test_serialize() -> Result<()> {
let store = get_store();
let wat = r#"
(module
(func $hello (import "" "hello"))
(func (export "run") (call $hello))
)
"#;
let module = Module::new(&store, wat)?;
let serialized_bytes = module.serialize()?;
assert!(!serialized_bytes.is_empty());
Ok(())
}
#[test]
fn test_deserialize() -> Result<()> {
let store = get_store();
let wat = r#"
(module $name
(import "host" "sum_part" (func (param i32 i64 i32 f32 f64) (result i64)))
(func (export "test_call") (result i64)
i32.const 100
i64.const 200
i32.const 300
f32.const 400
f64.const 500
call 0
)
)
"#;
let module = Module::new(&store, wat)?;
let serialized_bytes = module.serialize()?;
let headless_store = get_headless_store();
let deserialized_module = unsafe { Module::deserialize(&headless_store, &serialized_bytes)? };
assert_eq!(deserialized_module.name(), Some("name"));
assert_eq!(
deserialized_module.exports().collect::<Vec<_>>(),
module.exports().collect::<Vec<_>>()
);
assert_eq!(
deserialized_module.imports().collect::<Vec<_>>(),
module.imports().collect::<Vec<_>>()
);
let func_type = FunctionType::new(
vec![Type::I32, Type::I64, Type::I32, Type::F32, Type::F64],
vec![Type::I64],
);
let instance = Instance::new(
&module,
&imports! {
"host" => {
"sum_part" => Function::new_dynamic(&store, &func_type, |params| {
let param_0: i64 = params[0].unwrap_i32() as i64;
let param_1: i64 = params[1].unwrap_i64() as i64;
let param_2: i64 = params[2].unwrap_i32() as i64;
let param_3: i64 = params[3].unwrap_f32() as i64;
let param_4: i64 = params[4].unwrap_f64() as i64;
Ok(vec![Value::I64(param_0 + param_1 + param_2 + param_3 + param_4)])
})
}
},
)?;
let test_call = instance.exports.get_function("test_call")?;
let result = test_call.call(&[])?;
assert_eq!(result.to_vec(), vec![Value::I64(1500)]);
Ok(())
}

473
tests/compilers/traps.rs Normal file
View File

@@ -0,0 +1,473 @@
use crate::utils::get_store;
use anyhow::Result;
use std::panic::{self, AssertUnwindSafe};
use wasmer::*;
#[test]
fn test_trap_return() -> Result<()> {
let store = get_store();
let wat = r#"
(module
(func $hello (import "" "hello"))
(func (export "run") (call $hello))
)
"#;
let module = Module::new(&store, wat)?;
let hello_type = FunctionType::new(vec![], vec![]);
let hello_func =
Function::new_dynamic(&store, &hello_type, |_| Err(RuntimeError::new("test 123")));
let instance = Instance::new(
&module,
&imports! {
"" => {
"hello" => hello_func
}
},
)?;
let run_func = instance
.exports
.get_function("run")
.expect("expected function export");
let e = run_func.call(&[]).err().expect("error calling function");
assert_eq!(e.message(), "test 123");
Ok(())
}
#[test]
#[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)]
fn test_trap_trace() -> Result<()> {
let store = get_store();
let wat = r#"
(module $hello_mod
(func (export "run") (call $hello))
(func $hello (unreachable))
)
"#;
let module = Module::new(&store, wat)?;
let instance = Instance::new(&module, &imports! {})?;
let run_func = instance
.exports
.get_function("run")
.expect("expected function export");
let e = run_func.call(&[]).err().expect("error calling function");
let trace = e.trace();
assert_eq!(trace.len(), 2);
assert_eq!(trace[0].module_name(), "hello_mod");
assert_eq!(trace[0].func_index(), 1);
assert_eq!(trace[0].func_name(), Some("hello"));
assert_eq!(trace[1].module_name(), "hello_mod");
assert_eq!(trace[1].func_index(), 0);
assert_eq!(trace[1].func_name(), None);
assert!(
e.message().contains("unreachable"),
"wrong message: {}",
e.message()
);
Ok(())
}
#[test]
fn test_trap_trace_cb() -> Result<()> {
let store = get_store();
let wat = r#"
(module $hello_mod
(import "" "throw" (func $throw))
(func (export "run") (call $hello))
(func $hello (call $throw))
)
"#;
let fn_type = FunctionType::new(vec![], vec![]);
let fn_func = Function::new_dynamic(&store, &fn_type, |_| Err(RuntimeError::new("cb throw")));
let module = Module::new(&store, wat)?;
let instance = Instance::new(
&module,
&imports! {
"" => {
"throw" => fn_func
}
},
)?;
let run_func = instance
.exports
.get_function("run")
.expect("expected function export");
let e = run_func.call(&[]).err().expect("error calling function");
let trace = e.trace();
println!("Trace {:?}", trace);
// TODO: Reenable this (disabled as it was not working with llvm/singlepass)
// assert_eq!(trace.len(), 2);
// assert_eq!(trace[0].module_name(), "hello_mod");
// assert_eq!(trace[0].func_index(), 2);
// assert_eq!(trace[1].module_name(), "hello_mod");
// assert_eq!(trace[1].func_index(), 1);
assert_eq!(e.message(), "cb throw");
Ok(())
}
#[test]
#[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)]
fn test_trap_stack_overflow() -> Result<()> {
let store = get_store();
let wat = r#"
(module $rec_mod
(func $run (export "run") (call $run))
)
"#;
let module = Module::new(&store, wat)?;
let instance = Instance::new(&module, &imports! {})?;
let run_func = instance
.exports
.get_function("run")
.expect("expected function export");
let e = run_func.call(&[]).err().expect("error calling function");
let trace = e.trace();
assert!(trace.len() >= 32);
for i in 0..trace.len() {
assert_eq!(trace[i].module_name(), "rec_mod");
assert_eq!(trace[i].func_index(), 0);
assert_eq!(trace[i].func_name(), Some("run"));
}
assert!(e.message().contains("call stack exhausted"));
Ok(())
}
#[test]
#[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)]
fn trap_display_pretty() -> Result<()> {
let store = get_store();
let wat = r#"
(module $m
(func $die unreachable)
(func call $die)
(func $foo call 1)
(func (export "bar") call $foo)
)
"#;
let module = Module::new(&store, wat)?;
let instance = Instance::new(&module, &imports! {})?;
let run_func = instance
.exports
.get_function("bar")
.expect("expected function export");
let e = run_func.call(&[]).err().expect("error calling function");
assert_eq!(
e.to_string(),
"\
RuntimeError: unreachable
at die (m[0]:0x23)
at <unnamed> (m[1]:0x27)
at foo (m[2]:0x2c)
at <unnamed> (m[3]:0x31)"
);
Ok(())
}
#[test]
#[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)]
fn trap_display_multi_module() -> Result<()> {
let store = get_store();
let wat = r#"
(module $a
(func $die unreachable)
(func call $die)
(func $foo call 1)
(func (export "bar") call $foo)
)
"#;
let module = Module::new(&store, wat)?;
let instance = Instance::new(&module, &imports! {})?;
let bar = instance.exports.get_function("bar")?.clone();
let wat = r#"
(module $b
(import "" "" (func $bar))
(func $middle call $bar)
(func (export "bar2") call $middle)
)
"#;
let module = Module::new(&store, wat)?;
let instance = Instance::new(
&module,
&imports! {
"" => {
"" => bar
}
},
)?;
let bar2 = instance
.exports
.get_function("bar2")
.expect("expected function export");
let e = bar2.call(&[]).err().expect("error calling function");
assert_eq!(
e.to_string(),
"\
RuntimeError: unreachable
at die (a[0]:0x23)
at <unnamed> (a[1]:0x27)
at foo (a[2]:0x2c)
at <unnamed> (a[3]:0x31)
at middle (b[1]:0x29)
at <unnamed> (b[2]:0x2e)"
);
Ok(())
}
#[test]
fn trap_start_function_import() -> Result<()> {
let store = get_store();
let binary = r#"
(module $a
(import "" "" (func $foo))
(start $foo)
)
"#;
let module = Module::new(&store, &binary)?;
let sig = FunctionType::new(vec![], vec![]);
let func = Function::new_dynamic(&store, &sig, |_| Err(RuntimeError::new("user trap")));
let err = Instance::new(
&module,
&imports! {
"" => {
"" => func
}
},
)
.err()
.unwrap();
match err {
InstantiationError::Link(_) => panic!("It should be a start error"),
InstantiationError::Start(err) => {
assert_eq!(err.message(), "user trap");
}
}
Ok(())
}
#[test]
fn rust_panic_import() -> Result<()> {
let store = get_store();
let binary = r#"
(module $a
(import "" "foo" (func $foo))
(import "" "bar" (func $bar))
(func (export "foo") call $foo)
(func (export "bar") call $bar)
)
"#;
let module = Module::new(&store, &binary)?;
let sig = FunctionType::new(vec![], vec![]);
let func = Function::new_dynamic(&store, &sig, |_| panic!("this is a panic"));
let instance = Instance::new(
&module,
&imports! {
"" => {
"foo" => func,
"bar" => Function::new(&store, || panic!("this is another panic"))
}
},
)?;
let func = instance.exports.get_function("foo")?.clone();
let err = panic::catch_unwind(AssertUnwindSafe(|| {
drop(func.call(&[]));
}))
.unwrap_err();
assert_eq!(err.downcast_ref::<&'static str>(), Some(&"this is a panic"));
// TODO: Reenable this (disabled as it was not working with llvm/singlepass)
// It doesn't work either with cranelift and `--test-threads=1`.
// let func = instance.exports.get_function("bar")?.clone();
// let err = panic::catch_unwind(AssertUnwindSafe(|| {
// drop(func.call(&[]));
// }))
// .unwrap_err();
// assert_eq!(
// err.downcast_ref::<&'static str>(),
// Some(&"this is another panic")
// );
Ok(())
}
#[test]
fn rust_panic_start_function() -> Result<()> {
let store = get_store();
let binary = r#"
(module $a
(import "" "" (func $foo))
(start $foo)
)
"#;
let module = Module::new(&store, &binary)?;
let sig = FunctionType::new(vec![], vec![]);
let func = Function::new_dynamic(&store, &sig, |_| panic!("this is a panic"));
let err = panic::catch_unwind(AssertUnwindSafe(|| {
drop(Instance::new(
&module,
&imports! {
"" => {
"" => func
}
},
));
}))
.unwrap_err();
assert_eq!(err.downcast_ref::<&'static str>(), Some(&"this is a panic"));
let func = Function::new(&store, || panic!("this is another panic"));
let err = panic::catch_unwind(AssertUnwindSafe(|| {
drop(Instance::new(
&module,
&imports! {
"" => {
"" => func
}
},
));
}))
.unwrap_err();
assert_eq!(
err.downcast_ref::<&'static str>(),
Some(&"this is another panic")
);
Ok(())
}
#[test]
fn mismatched_arguments() -> Result<()> {
let store = get_store();
let binary = r#"
(module $a
(func (export "foo") (param i32))
)
"#;
let module = Module::new(&store, &binary)?;
let instance = Instance::new(&module, &imports! {})?;
let func: &Function = instance.exports.get("foo")?;
assert_eq!(
func.call(&[]).unwrap_err().message(),
"Parameters of type [] did not match signature [I32] -> []"
);
assert_eq!(
func.call(&[Val::F32(0.0)]).unwrap_err().message(),
"Parameters of type [F32] did not match signature [I32] -> []",
);
assert_eq!(
func.call(&[Val::I32(0), Val::I32(1)])
.unwrap_err()
.message(),
"Parameters of type [I32, I32] did not match signature [I32] -> []"
);
Ok(())
}
#[test]
#[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)]
fn call_signature_mismatch() -> Result<()> {
let store = get_store();
let binary = r#"
(module $a
(func $foo
i32.const 0
call_indirect)
(func $bar (param i32))
(start $foo)
(table 1 anyfunc)
(elem (i32.const 0) 1)
)
"#;
let module = Module::new(&store, &binary)?;
let err = Instance::new(&module, &imports! {})
.err()
.expect("expected error");
assert_eq!(
format!("{}", err),
"\
RuntimeError: indirect call type mismatch
at foo (a[0]:0x30)\
"
);
Ok(())
}
#[ignore]
#[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)]
fn start_trap_pretty() -> Result<()> {
let store = get_store();
let wat = r#"
(module $m
(func $die unreachable)
(func call $die)
(func $foo call 1)
(func $start call $foo)
(start $start)
)
"#;
let module = Module::new(&store, wat)?;
let err = Instance::new(&module, &imports! {})
.err()
.expect("expected error");
assert_eq!(
format!("{}", err),
"\
RuntimeError: unreachable
at die (m[0]:0x1d)
at <unnamed> (m[1]:0x21)
at foo (m[2]:0x26)
at start (m[3]:0x2b)\
"
);
Ok(())
}
#[test]
fn present_after_module_drop() -> Result<()> {
let store = get_store();
let module = Module::new(&store, r#"(func (export "foo") unreachable)"#)?;
let instance = Instance::new(&module, &imports! {})?;
let func: Function = instance.exports.get_function("foo")?.clone();
println!("asserting before we drop modules");
assert_trap(func.call(&[]).unwrap_err());
drop((instance, module));
println!("asserting after drop");
assert_trap(func.call(&[]).unwrap_err());
return Ok(());
fn assert_trap(t: RuntimeError) {
println!("{}", t);
// assert_eq!(t.trace().len(), 1);
// assert_eq!(t.trace()[0].func_index(), 0);
}
}

39
tests/compilers/utils.rs Normal file
View File

@@ -0,0 +1,39 @@
use std::sync::Arc;
use test_utils::get_compiler_config_from_str;
use wasmer::{Features, Store, Triple, Tunables};
use wasmer_engine_jit::JITEngine;
fn get_compiler_str() -> &'static str {
cfg_if::cfg_if! {
if #[cfg(any(
all(feature = "test-llvm", any(feature = "test-cranelift", feature = "test-singlepass")),
all(feature = "test-cranelift", feature = "test-singlepass")
))] {
compile_error!("Only one compiler can be selected")
} else if #[cfg(feature = "test-cranelift")] {
"cranelift"
} else if #[cfg(feature = "test-llvm")] {
"llvm"
} else if #[cfg(feature = "test-singlepass")] {
"singlepass"
} else {
compile_error!("No compiler chosen for the tests")
}
}
}
pub fn get_store() -> Store {
let features = Features::default();
let try_nan_canonicalization = false;
let compiler_config =
get_compiler_config_from_str(get_compiler_str(), try_nan_canonicalization, features);
let tunables = Tunables::for_target(compiler_config.target().triple());
let store = Store::new(Arc::new(JITEngine::new(compiler_config, tunables)));
store
}
pub fn get_headless_store() -> Store {
let tunables = Tunables::for_target(&Triple::host());
let store = Store::new(Arc::new(JITEngine::headless(tunables)));
store
}

View File

@@ -1,16 +1,16 @@
[package]
name = "wasmer-engine-dummy"
version = "0.16.2"
version = "1.0.0-alpha.1"
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
description = "wasmer testing utils"
edition = "2018"
publish = false
[dependencies]
wasm-common = { path = "../../../lib/wasm-common", version = "0.16.2" }
wasmer-compiler = { path = "../../../lib/compiler", version = "0.16.2" }
wasmer-runtime = { path = "../../../lib/runtime", version = "0.16.2" }
wasmer-engine = { path = "../../../lib/engine", version = "0.16.2" }
wasm-common = { path = "../../../lib/wasm-common", version = "1.0.0-alpha.1" }
wasmer-compiler = { path = "../../../lib/compiler", version = "1.0.0-alpha.1" }
wasmer-runtime = { path = "../../../lib/runtime", version = "1.0.0-alpha.1" }
wasmer-engine = { path = "../../../lib/engine", version = "1.0.0-alpha.1" }
serde = { version = "1.0", features = ["derive", "rc"], optional = true }
serde_bytes = { version = "0.11", optional = true }
bincode = { version = "1.2", optional = true }

View File

@@ -4,7 +4,7 @@ use crate::DummyArtifact;
use std::sync::Arc;
use wasm_common::FunctionType;
use wasmer_compiler::{CompileError, Features};
use wasmer_engine::{Artifact, DeserializeError, Engine, Tunables};
use wasmer_engine::{Artifact, DeserializeError, Engine, EngineId, Tunables};
use wasmer_runtime::{
SignatureRegistry, VMContext, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline,
};
@@ -23,6 +23,7 @@ pub struct DummyEngine {
signatures: Arc<SignatureRegistry>,
features: Arc<Features>,
tunables: Arc<dyn Tunables + Send + Sync>,
engine_id: EngineId,
}
impl DummyEngine {
@@ -32,6 +33,7 @@ impl DummyEngine {
signatures: Arc::new(SignatureRegistry::new()),
tunables: Arc::new(tunables),
features: Arc::new(Default::default()),
engine_id: EngineId::default(),
}
}
@@ -97,4 +99,8 @@ impl Engine for DummyEngine {
unsafe fn deserialize(&self, bytes: &[u8]) -> Result<Arc<dyn Artifact>, DeserializeError> {
Ok(Arc::new(DummyArtifact::deserialize(&self, &bytes)?))
}
fn id(&self) -> &EngineId {
&self.engine_id
}
}

View File

@@ -169,7 +169,7 @@ pub fn with_features(
f: impl Fn(&mut Testsuite) -> anyhow::Result<()> + Copy,
) -> anyhow::Result<()> {
for compiler in features.iter() {
writeln!(out.buffer, "#[cfg(feature=\"{}\")]", compiler)?;
writeln!(out.buffer, "#[cfg(feature=\"test-{}\")]", compiler)?;
writeln!(out.buffer, "#[cfg(test)]")?;
writeln!(out.buffer, "#[allow(non_snake_case)]")?;
with_test_module(&mut out, &compiler, f)?;

View File

@@ -20,11 +20,7 @@ pub fn wast_processor(out: &mut Testsuite, p: PathBuf) -> Option<Test> {
let compiler = out.path.get(0).unwrap();
// The implementation of `run_wast` lives in /tests/spectest.rs
let body = format!(
"crate::wast::run_wast(r#\"{}\"#, \"{}\")",
p.display(),
compiler
);
let body = format!("crate::run_wast(r#\"{}\"#, \"{}\")", p.display(), compiler);
Some(Test {
name: testname,

View File

@@ -1,6 +1,6 @@
[package]
name = "test-utils"
version = "0.16.2"
version = "1.0.0-alpha.1"
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
description = "wasmer testing utils"
edition = "2018"
@@ -9,12 +9,12 @@ publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
wasmer-compiler = { path = "../../../lib/compiler", version = "0.16.2" }
wasmer-compiler-singlepass = { path = "../../../lib/compiler-singlepass", version = "0.16.2", optional = true }
wasmer-compiler-cranelift = { path = "../../../lib/compiler-cranelift", version = "0.16.2", optional = true }
wasmer-compiler-llvm = { path = "../../../lib/compiler-llvm", version = "0.16.2", optional = true }
wasmer-engine-jit = { path = "../../../lib/engine-jit", version = "0.16.2" }
wasmer = { path = "../../../lib/api", version = "0.16.2", default-features = false }
wasmer-compiler = { path = "../../../lib/compiler", version = "1.0.0-alpha.1" }
wasmer-compiler-singlepass = { path = "../../../lib/compiler-singlepass", version = "1.0.0-alpha.1", optional = true }
wasmer-compiler-cranelift = { path = "../../../lib/compiler-cranelift", version = "1.0.0-alpha.1", optional = true }
wasmer-compiler-llvm = { path = "../../../lib/compiler-llvm", version = "1.0.0-alpha.1", optional = true }
wasmer-engine-jit = { path = "../../../lib/engine-jit", version = "1.0.0-alpha.1" }
wasmer = { path = "../../../lib/api", version = "1.0.0-alpha.1", default-features = false }
[features]
compiler = []

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