mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-16 17:18:57 +00:00
Remove engine-staticlib
This commit is contained in:
23
Cargo.lock
generated
23
Cargo.lock
generated
@@ -2851,7 +2851,6 @@ dependencies = [
|
||||
"wasmer-compiler-singlepass",
|
||||
"wasmer-emscripten",
|
||||
"wasmer-engine",
|
||||
"wasmer-engine-staticlib",
|
||||
"wasmer-engine-universal",
|
||||
"wasmer-middlewares",
|
||||
"wasmer-types",
|
||||
@@ -2896,7 +2895,6 @@ dependencies = [
|
||||
"wasmer-compiler-singlepass",
|
||||
"wasmer-emscripten",
|
||||
"wasmer-engine",
|
||||
"wasmer-engine-staticlib",
|
||||
"wasmer-engine-universal",
|
||||
"wasmer-types",
|
||||
"wasmer-vfs",
|
||||
@@ -3063,26 +3061,6 @@ dependencies = [
|
||||
"wasmer-vm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmer-engine-staticlib"
|
||||
version = "2.3.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"cfg-if 1.0.0",
|
||||
"enumset",
|
||||
"leb128",
|
||||
"libloading",
|
||||
"serde",
|
||||
"tempfile",
|
||||
"tracing",
|
||||
"wasmer-artifact",
|
||||
"wasmer-compiler",
|
||||
"wasmer-engine",
|
||||
"wasmer-object",
|
||||
"wasmer-types",
|
||||
"wasmer-vm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmer-engine-universal"
|
||||
version = "2.3.0"
|
||||
@@ -3324,7 +3302,6 @@ dependencies = [
|
||||
"wasmer-emscripten",
|
||||
"wasmer-engine",
|
||||
"wasmer-engine-dummy",
|
||||
"wasmer-engine-staticlib",
|
||||
"wasmer-engine-universal",
|
||||
"wasmer-middlewares",
|
||||
"wasmer-types",
|
||||
|
||||
@@ -18,7 +18,6 @@ wasmer-compiler-llvm = { version = "=2.3.0", path = "lib/compiler-llvm", optiona
|
||||
wasmer-emscripten = { version = "=2.3.0", path = "lib/emscripten", optional = true }
|
||||
wasmer-engine = { version = "=2.3.0", path = "lib/engine" }
|
||||
wasmer-engine-universal = { version = "=2.3.0", path = "lib/engine-universal", optional = true }
|
||||
wasmer-engine-staticlib = { version = "=2.3.0", path = "lib/engine-staticlib", optional = true }
|
||||
wasmer-wasi = { version = "=2.3.0", path = "lib/wasi", optional = true }
|
||||
wasmer-wast = { version = "=2.3.0", path = "tests/lib/wast", optional = true }
|
||||
wasi-test-generator = { version = "=2.3.0", path = "tests/wasi-wast", optional = true }
|
||||
@@ -42,7 +41,6 @@ members = [
|
||||
"lib/emscripten",
|
||||
"lib/engine",
|
||||
"lib/engine-universal",
|
||||
"lib/engine-staticlib",
|
||||
"lib/object",
|
||||
"lib/vfs",
|
||||
"lib/vnet",
|
||||
@@ -89,7 +87,6 @@ default = [
|
||||
"wat",
|
||||
"wast",
|
||||
"universal",
|
||||
"staticlib",
|
||||
"cache",
|
||||
"wasi",
|
||||
"emscripten",
|
||||
@@ -100,10 +97,6 @@ universal = [
|
||||
"wasmer-engine-universal",
|
||||
"engine",
|
||||
]
|
||||
staticlib = [
|
||||
"wasmer-engine-staticlib",
|
||||
"engine",
|
||||
]
|
||||
cache = ["wasmer-cache"]
|
||||
wast = ["wasmer-wast"]
|
||||
wasi = ["wasmer-wasi"]
|
||||
@@ -113,7 +106,6 @@ compiler = [
|
||||
"wasmer/compiler",
|
||||
"wasmer-compiler/translator",
|
||||
"wasmer-engine-universal/compiler",
|
||||
"wasmer-engine-staticlib/compiler",
|
||||
]
|
||||
singlepass = [
|
||||
"wasmer-compiler-singlepass",
|
||||
|
||||
39
Makefile
39
Makefile
@@ -13,7 +13,7 @@ SHELL=/usr/bin/env bash
|
||||
# | Compiler ⨯ Engine ⨯ Platform ⨯ Architecture ⨯ libc |
|
||||
# |------------|-----------|----------|--------------|-------|
|
||||
# | Cranelift | Universal | Linux | amd64 | glibc |
|
||||
# | LLVM | Staticlib | Darwin | aarch64 | musl |
|
||||
# | LLVM | | Darwin | aarch64 | musl |
|
||||
# | Singlepass | | Windows | | |
|
||||
# |------------|-----------|----------|--------------|-------|
|
||||
#
|
||||
@@ -359,7 +359,7 @@ check-wasmer-wasm:
|
||||
|
||||
check-capi: capi-setup
|
||||
RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) check $(CARGO_TARGET) --manifest-path lib/c-api/Cargo.toml \
|
||||
--no-default-features --features wat,universal,dylib,staticlib,wasi,middlewares $(capi_compiler_features)
|
||||
--no-default-features --features wat,universal,wasi,middlewares $(capi_compiler_features)
|
||||
|
||||
build-wasmer:
|
||||
$(CARGO_BINARY) build $(CARGO_TARGET) --release --manifest-path lib/cli/Cargo.toml $(compiler_features) --bin wasmer
|
||||
@@ -421,62 +421,46 @@ build-docs-capi: capi-setup
|
||||
# when generating the documentation, we rename it to its
|
||||
# crate's name. Then we restore the lib's name.
|
||||
sed "$(SEDI)" -e 's/name = "wasmer" # ##lib.name##/name = "wasmer_c_api" # ##lib.name##/' lib/c-api/Cargo.toml
|
||||
RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) doc $(CARGO_TARGET) --manifest-path lib/c-api/Cargo.toml --no-deps --features wat,universal,staticlib,cranelift,wasi
|
||||
RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) doc $(CARGO_TARGET) --manifest-path lib/c-api/Cargo.toml --no-deps --features wat,universal,cranelift,wasi
|
||||
sed "$(SEDI)" -e 's/name = "wasmer_c_api" # ##lib.name##/name = "wasmer" # ##lib.name##/' lib/c-api/Cargo.toml
|
||||
|
||||
build-capi: capi-setup
|
||||
RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET) --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features wat,universal,staticlib,wasi,middlewares $(capi_compiler_features)
|
||||
--no-default-features --features wat,universal,wasi,middlewares $(capi_compiler_features)
|
||||
|
||||
build-capi-singlepass: capi-setup
|
||||
RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET) --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features wat,universal,staticlib,singlepass,wasi,middlewares
|
||||
--no-default-features --features wat,universal,singlepass,wasi,middlewares
|
||||
|
||||
build-capi-singlepass-universal: capi-setup
|
||||
RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET) --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features wat,universal,singlepass,wasi,middlewares
|
||||
|
||||
build-capi-singlepass-staticlib: capi-setup
|
||||
RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET) --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features wat,staticlib,singlepass,wasi,middlewares
|
||||
|
||||
build-capi-cranelift: capi-setup
|
||||
RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET) --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features wat,universal,staticlib,cranelift,wasi,middlewares
|
||||
--no-default-features --features wat,universal,cranelift,wasi,middlewares
|
||||
|
||||
build-capi-cranelift-universal: capi-setup
|
||||
RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET) --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features wat,universal,cranelift,wasi,middlewares
|
||||
|
||||
build-capi-cranelift-staticlib: capi-setup
|
||||
RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET) --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features wat,staticlib,cranelift,wasi,middlewares
|
||||
|
||||
build-capi-llvm: capi-setup
|
||||
RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET) --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features wat,universal,staticlib,llvm,wasi,middlewares
|
||||
--no-default-features --features wat,universal,llvm,wasi,middlewares
|
||||
|
||||
build-capi-llvm-universal: capi-setup
|
||||
RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET) --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features wat,universal,llvm,wasi,middlewares
|
||||
|
||||
build-capi-llvm-staticlib: capi-setup
|
||||
RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET) --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features wat,staticlib,llvm,wasi,middlewares
|
||||
|
||||
# Headless (we include the minimal to be able to run)
|
||||
|
||||
build-capi-headless-universal: capi-setup
|
||||
RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET) --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features universal,wasi
|
||||
|
||||
build-capi-headless-staticlib: capi-setup
|
||||
RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET) --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features staticlib,wasi
|
||||
|
||||
build-capi-headless-all: capi-setup
|
||||
RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET) --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features universal,staticlib,wasi
|
||||
--no-default-features --features universal,wasi
|
||||
|
||||
build-capi-headless-ios: capi-setup
|
||||
RUSTFLAGS="${RUSTFLAGS}" cargo lipo --manifest-path lib/c-api/Cargo.toml --release \
|
||||
@@ -536,7 +520,7 @@ test-capi: build-capi package-capi $(foreach compiler_engine,$(capi_compilers_en
|
||||
|
||||
test-capi-crate-%:
|
||||
WASMER_CAPI_CONFIG=$(shell echo $@ | sed -e s/test-capi-crate-//) $(CARGO_BINARY) test $(CARGO_TARGET) --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features wat,universal,staticlib,wasi,middlewares $(capi_compiler_features) -- --nocapture
|
||||
--no-default-features --features wat,universal,wasi,middlewares $(capi_compiler_features) -- --nocapture
|
||||
|
||||
test-capi-integration-%:
|
||||
# Test the Wasmer C API tests for C
|
||||
@@ -662,7 +646,7 @@ endif
|
||||
|
||||
DESTDIR ?= /usr/local
|
||||
|
||||
install: install-wasmer install-capi-headers install-capi-lib install-capi-staticlib install-pkgconfig install-misc
|
||||
install: install-wasmer install-capi-headers install-capi-lib install-pkgconfig install-misc
|
||||
|
||||
install-wasmer:
|
||||
install -Dm755 target/release/wasmer $(DESTDIR)/bin/wasmer
|
||||
@@ -681,9 +665,6 @@ install-capi-lib:
|
||||
ln -sf "libwasmer.so.$$pkgver" "$(DESTDIR)/lib/libwasmer.so.$$majorver" && \
|
||||
ln -sf "libwasmer.so.$$pkgver" "$(DESTDIR)/lib/libwasmer.so"
|
||||
|
||||
install-capi-staticlib:
|
||||
install -Dm644 target/release/libwasmer.a "$(DESTDIR)/lib/libwasmer.a"
|
||||
|
||||
install-misc:
|
||||
install -Dm644 LICENSE "$(DESTDIR)"/share/licenses/wasmer/LICENSE
|
||||
|
||||
|
||||
@@ -34,11 +34,6 @@ composed of a set of crates. We can group them as follows:
|
||||
differ:
|
||||
* `engine-universal` — stores the code in a custom file format, and
|
||||
loads it in memory,
|
||||
* `engine-staticlib` — stores executable code in a native static
|
||||
object library, in addition to emitting a C header file, which
|
||||
both can be linked against a sandboxed WebAssembly runtime
|
||||
environment for the compiled module with no need for runtime
|
||||
compilation,
|
||||
* `object` — A library to cross-generate native objects for various
|
||||
platforms.
|
||||
* `middlewares` — A collection of middlewares, like `metering` that
|
||||
|
||||
@@ -49,10 +49,6 @@ Wasmer is not only fast, but also designed to be *highly customizable*:
|
||||
compilation process and to store the generated executable code
|
||||
somewhere, either:
|
||||
* in-memory (with [`wasmer-engine-universal`]),
|
||||
* in a native static object file (with [`wasmer-engine-staticlib`]),
|
||||
in addition to emitting a C header file, which both can be linked
|
||||
against a sandboxed WebAssembly runtime environment for the
|
||||
compiled module with no need for runtime compilation.
|
||||
|
||||
* **Pluggable compilers** — A compiler is used by an engine to
|
||||
transform WebAssembly into executable code:
|
||||
@@ -99,7 +95,6 @@ more](https://wasmerio.github.io/wasmer/crates/doc/wasmer/).
|
||||
Made with ❤️ by the Wasmer team, for the community
|
||||
|
||||
[`wasmer-engine-universal`]: https://github.com/wasmerio/wasmer/tree/master/lib/engine-universal
|
||||
[`wasmer-engine-staticlib`]: https://github.com/wasmerio/wasmer/tree/master/lib/engine-staticlib
|
||||
[`wasmer-compiler-singlepass`]: https://github.com/wasmerio/wasmer/tree/master/lib/compiler-singlepass
|
||||
[`wasmer-compiler-cranelift`]: https://github.com/wasmerio/wasmer/tree/master/lib/compiler-cranelift
|
||||
[`wasmer-compiler-llvm`]: https://github.com/wasmerio/wasmer/tree/master/lib/compiler-llvm
|
||||
|
||||
@@ -77,10 +77,6 @@
|
||||
//! compilation process and to store the generated executable code
|
||||
//! somewhere, either:
|
||||
//! * in-memory (with [`wasmer-engine-universal`]),
|
||||
//! * in a native static object file (with [`wasmer-engine-staticlib`]),
|
||||
//! in addition to emitting a C header file, which both can be linked
|
||||
//! against a sandboxed WebAssembly runtime environment for the
|
||||
//! compiled module with no need for runtime compilation.
|
||||
//!
|
||||
//! * **Pluggable compilers** — A compiler is used by an engine to
|
||||
//! transform WebAssembly into executable code:
|
||||
@@ -424,7 +420,6 @@
|
||||
//! [`wasmer-emscripten`]: https://docs.rs/wasmer-emscripten/
|
||||
//! [wasmer-engine]: https://docs.rs/wasmer-engine/
|
||||
//! [`wasmer-engine-universal`]: https://docs.rs/wasmer-engine-universal/
|
||||
//! [`wasmer-engine-staticlib`]: https://docs.rs/wasmer-engine-staticlib/
|
||||
//! [`wasmer-compiler-singlepass`]: https://docs.rs/wasmer-compiler-singlepass/
|
||||
//! [`wasmer-compiler-llvm`]: https://docs.rs/wasmer-compiler-llvm/
|
||||
//! [`wasmer-compiler-cranelift`]: https://docs.rs/wasmer-compiler-cranelift/
|
||||
|
||||
@@ -13,7 +13,6 @@ attributions of the project.
|
||||
|
||||
|
||||
[`wasmer-engine-universal`]: https://github.com/wasmerio/wasmer/tree/master/lib/engine-universal
|
||||
[`wasmer-engine-staticlib`]: https://github.com/wasmerio/wasmer/tree/master/lib/engine-staticlib
|
||||
[`wasmer-engine-dummy`]: https://github.com/wasmerio/wasmer/tree/master/tests/lib/engine-dummy
|
||||
[`wasmtime-api`]: https://crates.io/crates/wasmtime
|
||||
[Wasmer `ATTRIBUTIONS`]: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md
|
||||
|
||||
@@ -29,7 +29,6 @@ wasmer-compiler-llvm = { version = "=2.3.0", path = "../compiler-llvm", optional
|
||||
wasmer-emscripten = { version = "=2.3.0", path = "../emscripten", optional = true }
|
||||
wasmer-engine = { version = "=2.3.0", path = "../engine" }
|
||||
wasmer-engine-universal = { version = "=2.3.0", path = "../engine-universal", optional = true }
|
||||
wasmer-engine-staticlib = { version = "=2.3.0", path = "../engine-staticlib", optional = true }
|
||||
wasmer-middlewares = { version = "=2.3.0", path = "../middlewares", optional = true }
|
||||
wasmer-wasi = { version = "=2.3.0", path = "../wasi", default-features = false, features = ["host-fs", "sys"], optional = true }
|
||||
wasmer-types = { version = "=2.3.0", path = "../types" }
|
||||
@@ -65,14 +64,9 @@ universal = [
|
||||
"wasmer-engine-universal",
|
||||
"engine",
|
||||
]
|
||||
staticlib = [
|
||||
"wasmer-engine-staticlib",
|
||||
"engine",
|
||||
]
|
||||
compiler = [
|
||||
"wasmer-api/compiler",
|
||||
"wasmer-engine-universal/compiler",
|
||||
"wasmer-engine-staticlib/compiler"
|
||||
]
|
||||
singlepass = [
|
||||
"wasmer-compiler-singlepass",
|
||||
|
||||
@@ -13,8 +13,6 @@ use crate::error::update_last_error;
|
||||
use cfg_if::cfg_if;
|
||||
use std::sync::Arc;
|
||||
use wasmer_api::Engine;
|
||||
#[cfg(feature = "staticlib")]
|
||||
use wasmer_engine_staticlib::Staticlib;
|
||||
#[cfg(feature = "universal")]
|
||||
use wasmer_engine_universal::Universal;
|
||||
|
||||
@@ -67,10 +65,6 @@ pub enum wasmer_engine_t {
|
||||
/// Variant to represent the Universal engine. See the
|
||||
/// [`wasmer_engine_universal`] Rust crate.
|
||||
UNIVERSAL = 0,
|
||||
|
||||
/// Variant to represent the Staticlib engine. See the
|
||||
/// [`wasmer_engine_staticlib`] Rust crate.
|
||||
STATICLIB = 2,
|
||||
}
|
||||
|
||||
impl Default for wasmer_engine_t {
|
||||
@@ -78,8 +72,6 @@ impl Default for wasmer_engine_t {
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "universal")] {
|
||||
Self::UNIVERSAL
|
||||
} else if #[cfg(feature = "staticlib")] {
|
||||
Self::STATICLIB
|
||||
} else {
|
||||
compile_error!("Please enable one of the engines")
|
||||
}
|
||||
@@ -320,22 +312,6 @@ cfg_if! {
|
||||
let engine: Arc<dyn Engine + Send + Sync> = Arc::new(Universal::headless().engine());
|
||||
Box::new(wasm_engine_t { inner: engine })
|
||||
}
|
||||
}
|
||||
// There are currently no uses of the Staticlib engine + compiler from the C API.
|
||||
// So if we get here, we default to headless mode regardless of if `compiler` is enabled.
|
||||
else if #[cfg(feature = "staticlib")] {
|
||||
/// Creates a new headless Staticlib engine.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// See [`wasm_engine_delete`].
|
||||
///
|
||||
/// cbindgen:ignore
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_engine_new() -> Box<wasm_engine_t> {
|
||||
let engine: Arc<dyn Engine + Send + Sync> = Arc::new(Staticlib::headless().engine());
|
||||
Box::new(wasm_engine_t { inner: engine })
|
||||
}
|
||||
} else {
|
||||
/// Creates a new unknown engine, i.e. it will panic with an error message.
|
||||
///
|
||||
@@ -463,27 +439,6 @@ pub extern "C" fn wasm_engine_new_with_config(
|
||||
}
|
||||
}
|
||||
},
|
||||
wasmer_engine_t::STATICLIB => {
|
||||
cfg_if! {
|
||||
// There are currently no uses of the Staticlib engine + compiler from the C API.
|
||||
// So we run in headless mode.
|
||||
if #[cfg(feature = "staticlib")] {
|
||||
let mut builder = Staticlib::headless();
|
||||
|
||||
if let Some(target) = config.target {
|
||||
builder = builder.target(target.inner);
|
||||
}
|
||||
|
||||
if let Some(features) = config.features {
|
||||
builder = builder.features(features.inner);
|
||||
}
|
||||
|
||||
Arc::new(builder.engine())
|
||||
} else {
|
||||
return return_with_error("Wasmer has not been compiled with the `staticlib` feature.");
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
Some(Box::new(wasm_engine_t { inner }))
|
||||
} else {
|
||||
@@ -507,25 +462,6 @@ pub extern "C" fn wasm_engine_new_with_config(
|
||||
}
|
||||
}
|
||||
},
|
||||
wasmer_engine_t::STATICLIB => {
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "staticlib")] {
|
||||
let mut builder = Staticlib::headless();
|
||||
|
||||
if let Some(target) = config.target {
|
||||
builder = builder.target(target.inner);
|
||||
}
|
||||
|
||||
if let Some(features) = config.features {
|
||||
builder = builder.features(features.inner);
|
||||
}
|
||||
|
||||
Arc::new(builder.engine())
|
||||
} else {
|
||||
return return_with_error("Wasmer has not been compiled with the `staticlib` feature.");
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
Some(Box::new(wasm_engine_t { inner }))
|
||||
}
|
||||
|
||||
@@ -163,7 +163,6 @@ pub extern "C" fn wasmer_is_headless() -> bool {
|
||||
pub extern "C" fn wasmer_is_engine_available(engine: wasmer_engine_t) -> bool {
|
||||
match engine {
|
||||
wasmer_engine_t::UNIVERSAL if cfg!(feature = "universal") => true,
|
||||
wasmer_engine_t::STATICLIB if cfg!(feature = "staticlib") => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -244,14 +243,6 @@ mod tests {
|
||||
"0"
|
||||
},
|
||||
);
|
||||
set_var(
|
||||
"STATICLIB",
|
||||
if cfg!(feature = "staticlib") {
|
||||
"1"
|
||||
} else {
|
||||
"0"
|
||||
},
|
||||
);
|
||||
|
||||
(assert_c! {
|
||||
#include "tests/wasmer.h"
|
||||
@@ -259,7 +250,6 @@ mod tests {
|
||||
|
||||
int main() {
|
||||
assert(wasmer_is_engine_available(UNIVERSAL) == (getenv("UNIVERSAL")[0] == '1'));
|
||||
assert(wasmer_is_engine_available(STATICLIB) == (getenv("STATICLIB")[0] == '1'));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -267,6 +257,5 @@ mod tests {
|
||||
.success();
|
||||
|
||||
remove_var("UNIVERSAL");
|
||||
remove_var("STATICLIB");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ wasmer-compiler-llvm = { version = "=2.3.0", path = "../compiler-llvm", optional
|
||||
wasmer-emscripten = { version = "=2.3.0", path = "../emscripten", optional = true }
|
||||
wasmer-engine = { version = "=2.3.0", path = "../engine" }
|
||||
wasmer-engine-universal = { version = "=2.3.0", path = "../engine-universal", optional = true }
|
||||
wasmer-engine-staticlib = { version = "=2.3.0", path = "../engine-staticlib", optional = true }
|
||||
wasmer-vm = { version = "=2.3.0", path = "../vm" }
|
||||
wasmer-wasi = { version = "=2.3.0", path = "../wasi", optional = true }
|
||||
wasmer-wasi-experimental-io-devices = { version = "=2.3.0", path = "../wasi-experimental-io-devices", optional = true }
|
||||
@@ -65,7 +64,6 @@ default = [
|
||||
"wat",
|
||||
"wast",
|
||||
"universal",
|
||||
"staticlib",
|
||||
"cache",
|
||||
"wasi",
|
||||
"emscripten",
|
||||
@@ -75,10 +73,6 @@ universal = [
|
||||
"wasmer-engine-universal",
|
||||
"engine",
|
||||
]
|
||||
staticlib = [
|
||||
"wasmer-engine-staticlib",
|
||||
"engine",
|
||||
]
|
||||
cache = ["wasmer-cache"]
|
||||
cache-blake3-pure = ["wasmer-cache/blake3-pure"]
|
||||
wast = ["wasmer-wast"]
|
||||
@@ -88,7 +82,6 @@ wat = ["wasmer/wat"]
|
||||
compiler = [
|
||||
"wasmer-compiler/translator",
|
||||
"wasmer-engine-universal/compiler",
|
||||
"wasmer-engine-staticlib/compiler",
|
||||
]
|
||||
experimental-io-devices = [
|
||||
"wasmer-wasi-experimental-io-devices",
|
||||
|
||||
@@ -1,548 +0,0 @@
|
||||
//! A convenient little abstraction for building up C expressions and generating
|
||||
//! simple C code.
|
||||
|
||||
pub mod staticlib_header;
|
||||
|
||||
/// An identifier in C.
|
||||
pub type CIdent = String;
|
||||
|
||||
/// A Type in the C language.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CType {
|
||||
/// C `void` type.
|
||||
Void,
|
||||
/// A pointer to some other type.
|
||||
PointerTo {
|
||||
/// Whether the pointer is `const`.
|
||||
is_const: bool,
|
||||
/// The type that the pointer points to.
|
||||
inner: Box<CType>,
|
||||
},
|
||||
/// C 8 bit unsigned integer type.
|
||||
U8,
|
||||
/// C 16 bit unsigned integer type.
|
||||
U16,
|
||||
/// C 32 bit unsigned integer type.
|
||||
U32,
|
||||
/// C 64 bit unsigned integer type.
|
||||
U64,
|
||||
/// C pointer sized unsigned integer type.
|
||||
USize,
|
||||
/// C 8 bit signed integer type.
|
||||
I8,
|
||||
/// C 16 bit signed integer type.
|
||||
I16,
|
||||
/// C 32 bit signed integer type.
|
||||
I32,
|
||||
/// C 64 bit signed integer type.
|
||||
I64,
|
||||
/// C pointer sized signed integer type.
|
||||
ISize,
|
||||
/// A function or function pointer.
|
||||
Function {
|
||||
/// The arguments the function takes.
|
||||
arguments: Vec<CType>,
|
||||
/// The return value if it has one
|
||||
///
|
||||
/// None is equivalent to Some(Box(Ctype::Void)).
|
||||
return_value: Option<Box<CType>>,
|
||||
},
|
||||
/// C constant array.
|
||||
Array {
|
||||
/// The type of the array.
|
||||
inner: Box<CType>,
|
||||
},
|
||||
/// A user defined type.
|
||||
TypeDef(String),
|
||||
}
|
||||
|
||||
impl CType {
|
||||
/// Convenience function to get a mutable void pointer type.
|
||||
pub fn void_ptr() -> Self {
|
||||
CType::PointerTo {
|
||||
is_const: false,
|
||||
inner: Box::new(CType::Void),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience function to get a const void pointer type.
|
||||
#[allow(dead_code)]
|
||||
pub fn const_void_ptr() -> Self {
|
||||
CType::PointerTo {
|
||||
is_const: true,
|
||||
inner: Box::new(CType::Void),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate the C source code for a type into the given `String`.
|
||||
fn generate_c(&self, w: &mut String) {
|
||||
match &self {
|
||||
Self::Void => {
|
||||
w.push_str("void");
|
||||
}
|
||||
Self::PointerTo { is_const, inner } => {
|
||||
if *is_const {
|
||||
w.push_str("const ");
|
||||
}
|
||||
inner.generate_c(w);
|
||||
w.push('*');
|
||||
}
|
||||
Self::U8 => {
|
||||
w.push_str("unsigned char");
|
||||
}
|
||||
Self::U16 => {
|
||||
w.push_str("unsigned short");
|
||||
}
|
||||
Self::U32 => {
|
||||
w.push_str("unsigned int");
|
||||
}
|
||||
Self::U64 => {
|
||||
w.push_str("unsigned long long");
|
||||
}
|
||||
Self::USize => {
|
||||
w.push_str("unsigned size_t");
|
||||
}
|
||||
Self::I8 => {
|
||||
w.push_str("char");
|
||||
}
|
||||
Self::I16 => {
|
||||
w.push_str("short");
|
||||
}
|
||||
Self::I32 => {
|
||||
w.push_str("int");
|
||||
}
|
||||
Self::I64 => {
|
||||
w.push_str("long long");
|
||||
}
|
||||
Self::ISize => {
|
||||
w.push_str("size_t");
|
||||
}
|
||||
Self::Function {
|
||||
arguments,
|
||||
return_value,
|
||||
} => {
|
||||
// function with no, name, assume it's a function pointer
|
||||
let ret: CType = return_value
|
||||
.as_ref()
|
||||
.map(|i| (**i).clone())
|
||||
.unwrap_or_default();
|
||||
ret.generate_c(w);
|
||||
w.push(' ');
|
||||
w.push_str("(*)");
|
||||
w.push('(');
|
||||
match arguments.len() {
|
||||
l if l > 1 => {
|
||||
for arg in &arguments[..arguments.len() - 1] {
|
||||
arg.generate_c(w);
|
||||
w.push_str(", ");
|
||||
}
|
||||
arguments.last().unwrap().generate_c(w);
|
||||
}
|
||||
1 => {
|
||||
arguments[0].generate_c(w);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
w.push(')');
|
||||
}
|
||||
Self::Array { inner } => {
|
||||
inner.generate_c(w);
|
||||
w.push_str("[]");
|
||||
}
|
||||
Self::TypeDef(inner) => {
|
||||
w.push_str(inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate the C source code for a type with a nameinto the given `String`.
|
||||
fn generate_c_with_name(&self, name: &str, w: &mut String) {
|
||||
match &self {
|
||||
Self::PointerTo { .. }
|
||||
| Self::TypeDef { .. }
|
||||
| Self::Void
|
||||
| Self::U8
|
||||
| Self::U16
|
||||
| Self::U32
|
||||
| Self::U64
|
||||
| Self::USize
|
||||
| Self::I8
|
||||
| Self::I16
|
||||
| Self::I32
|
||||
| Self::I64
|
||||
| Self::ISize => {
|
||||
self.generate_c(w);
|
||||
w.push(' ');
|
||||
w.push_str(name);
|
||||
}
|
||||
Self::Function {
|
||||
arguments,
|
||||
return_value,
|
||||
} => {
|
||||
let ret: CType = return_value
|
||||
.as_ref()
|
||||
.map(|i| (**i).clone())
|
||||
.unwrap_or_default();
|
||||
ret.generate_c(w);
|
||||
w.push(' ');
|
||||
w.push_str(name);
|
||||
w.push('(');
|
||||
match arguments.len() {
|
||||
l if l > 1 => {
|
||||
for arg in &arguments[..arguments.len() - 1] {
|
||||
arg.generate_c(w);
|
||||
w.push_str(", ");
|
||||
}
|
||||
arguments.last().unwrap().generate_c(w);
|
||||
}
|
||||
1 => {
|
||||
arguments[0].generate_c(w);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
w.push(')');
|
||||
}
|
||||
Self::Array { inner } => {
|
||||
inner.generate_c(w);
|
||||
w.push(' ');
|
||||
w.push_str(name);
|
||||
w.push_str("[]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CType {
|
||||
fn default() -> CType {
|
||||
CType::Void
|
||||
}
|
||||
}
|
||||
|
||||
/// A statement in the C programming language. This may not be exact to what an
|
||||
/// AST would look like or what the C standard says about the C language, it's
|
||||
/// simply a structed way to organize data for generating C code.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CStatement {
|
||||
/// A declaration of some kind.
|
||||
Declaration {
|
||||
/// The name of the thing being declared.
|
||||
name: CIdent,
|
||||
/// Whether the thing being declared is `extern`.
|
||||
is_extern: bool,
|
||||
/// Whether the thing being declared is `const`.
|
||||
is_const: bool,
|
||||
/// The type of the thing being declared.
|
||||
ctype: CType,
|
||||
/// The definition of the thing being declared.
|
||||
///
|
||||
/// This is useful for initializing constant arrays, for example.
|
||||
definition: Option<Box<CStatement>>,
|
||||
},
|
||||
|
||||
/// A literal array of CStatements.
|
||||
LiteralArray {
|
||||
/// The contents of the array.
|
||||
items: Vec<CStatement>,
|
||||
},
|
||||
|
||||
/// A literal constant value, passed through directly as a string.
|
||||
LiteralConstant {
|
||||
/// The raw value acting as a constant.
|
||||
value: String,
|
||||
},
|
||||
|
||||
/// A C-style cast
|
||||
Cast {
|
||||
/// The type to cast to.
|
||||
target_type: CType,
|
||||
/// The thing being cast.
|
||||
expression: Box<CStatement>,
|
||||
},
|
||||
|
||||
/// Typedef one type to another.
|
||||
TypeDef {
|
||||
/// The type of the thing being typedef'd.
|
||||
source_type: CType,
|
||||
/// The new name by which this type may be called.
|
||||
new_name: CIdent,
|
||||
},
|
||||
}
|
||||
|
||||
impl CStatement {
|
||||
/// Generate C source code for the given CStatement.
|
||||
fn generate_c(&self, w: &mut String) {
|
||||
match &self {
|
||||
Self::Declaration {
|
||||
name,
|
||||
is_extern,
|
||||
is_const,
|
||||
ctype,
|
||||
definition,
|
||||
} => {
|
||||
if *is_const {
|
||||
w.push_str("const ");
|
||||
}
|
||||
if *is_extern {
|
||||
w.push_str("extern ");
|
||||
}
|
||||
ctype.generate_c_with_name(name, w);
|
||||
if let Some(def) = definition {
|
||||
w.push_str(" = ");
|
||||
def.generate_c(w);
|
||||
}
|
||||
w.push(';');
|
||||
w.push('\n');
|
||||
}
|
||||
Self::LiteralArray { items } => {
|
||||
w.push('{');
|
||||
if !items.is_empty() {
|
||||
w.push('\n');
|
||||
}
|
||||
for item in items {
|
||||
w.push('\t');
|
||||
item.generate_c(w);
|
||||
w.push(',');
|
||||
w.push('\n');
|
||||
}
|
||||
w.push('}');
|
||||
}
|
||||
Self::LiteralConstant { value } => {
|
||||
w.push_str(value);
|
||||
}
|
||||
Self::Cast {
|
||||
target_type,
|
||||
expression,
|
||||
} => {
|
||||
w.push('(');
|
||||
target_type.generate_c(w);
|
||||
w.push(')');
|
||||
w.push(' ');
|
||||
expression.generate_c(w);
|
||||
}
|
||||
Self::TypeDef {
|
||||
source_type,
|
||||
new_name,
|
||||
} => {
|
||||
w.push_str("typedef ");
|
||||
// leaky abstraction / hack, doesn't fully solve the problem
|
||||
if let CType::Function { .. } = source_type {
|
||||
source_type.generate_c_with_name(&format!("(*{})", new_name), w);
|
||||
} else {
|
||||
source_type.generate_c(w);
|
||||
w.push(' ');
|
||||
w.push_str(new_name);
|
||||
}
|
||||
w.push(';');
|
||||
w.push('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate C source code from some `CStatements` into a String.
|
||||
// TODO: add config section
|
||||
pub fn generate_c(statements: &[CStatement]) -> String {
|
||||
let mut out = String::new();
|
||||
for statement in statements {
|
||||
statement.generate_c(&mut out);
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn generate_types() {
|
||||
macro_rules! assert_c_type {
|
||||
($ctype:expr, $expected:expr) => {
|
||||
let mut w = String::new();
|
||||
let ctype = $ctype;
|
||||
ctype.generate_c(&mut w);
|
||||
assert_eq!(w, $expected);
|
||||
};
|
||||
}
|
||||
|
||||
assert_c_type!(CType::Void, "void");
|
||||
assert_c_type!(CType::void_ptr(), "void*");
|
||||
assert_c_type!(CType::const_void_ptr(), "const void*");
|
||||
assert_c_type!(CType::U8, "unsigned char");
|
||||
assert_c_type!(CType::U16, "unsigned short");
|
||||
assert_c_type!(CType::U32, "unsigned int");
|
||||
assert_c_type!(CType::U64, "unsigned long long");
|
||||
assert_c_type!(CType::USize, "unsigned size_t");
|
||||
assert_c_type!(CType::I8, "char");
|
||||
assert_c_type!(CType::I16, "short");
|
||||
assert_c_type!(CType::I32, "int");
|
||||
assert_c_type!(CType::I64, "long long");
|
||||
assert_c_type!(CType::ISize, "size_t");
|
||||
assert_c_type!(CType::TypeDef("my_type".to_string()), "my_type");
|
||||
assert_c_type!(
|
||||
CType::Function {
|
||||
arguments: vec![CType::U8, CType::ISize],
|
||||
return_value: None
|
||||
},
|
||||
"void (*)(unsigned char, size_t)"
|
||||
);
|
||||
assert_c_type!(
|
||||
CType::Function {
|
||||
arguments: vec![],
|
||||
return_value: Some(Box::new(CType::ISize))
|
||||
},
|
||||
"size_t (*)()"
|
||||
);
|
||||
assert_c_type!(
|
||||
CType::PointerTo {
|
||||
is_const: true,
|
||||
inner: Box::new(CType::PointerTo {
|
||||
is_const: false,
|
||||
inner: Box::new(CType::U32)
|
||||
})
|
||||
},
|
||||
"const unsigned int**"
|
||||
);
|
||||
// TODO: test more complicated const correctness rules: there are bugs relating to it.
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_types_with_names() {
|
||||
macro_rules! assert_c_type {
|
||||
($ctype:expr, $name:literal, $expected:expr) => {
|
||||
let mut w = String::new();
|
||||
let ctype = $ctype;
|
||||
ctype.generate_c_with_name($name, &mut w);
|
||||
assert_eq!(w, $expected);
|
||||
};
|
||||
}
|
||||
|
||||
assert_c_type!(CType::Void, "main", "void main");
|
||||
assert_c_type!(CType::void_ptr(), "data", "void* data");
|
||||
assert_c_type!(CType::const_void_ptr(), "data", "const void* data");
|
||||
assert_c_type!(CType::U8, "data", "unsigned char data");
|
||||
assert_c_type!(CType::U16, "data", "unsigned short data");
|
||||
assert_c_type!(CType::U32, "data", "unsigned int data");
|
||||
assert_c_type!(CType::U64, "data", "unsigned long long data");
|
||||
assert_c_type!(CType::USize, "data", "unsigned size_t data");
|
||||
assert_c_type!(CType::I8, "data", "char data");
|
||||
assert_c_type!(CType::I16, "data", "short data");
|
||||
assert_c_type!(CType::I32, "data", "int data");
|
||||
assert_c_type!(CType::I64, "data", "long long data");
|
||||
assert_c_type!(CType::ISize, "data", "size_t data");
|
||||
assert_c_type!(
|
||||
CType::TypeDef("my_type".to_string()),
|
||||
"data",
|
||||
"my_type data"
|
||||
);
|
||||
assert_c_type!(
|
||||
CType::Function {
|
||||
arguments: vec![CType::U8, CType::ISize],
|
||||
return_value: None
|
||||
},
|
||||
"my_func",
|
||||
"void my_func(unsigned char, size_t)"
|
||||
);
|
||||
assert_c_type!(
|
||||
CType::Function {
|
||||
arguments: vec![],
|
||||
return_value: Some(Box::new(CType::ISize))
|
||||
},
|
||||
"my_func",
|
||||
"size_t my_func()"
|
||||
);
|
||||
assert_c_type!(
|
||||
CType::PointerTo {
|
||||
is_const: true,
|
||||
inner: Box::new(CType::PointerTo {
|
||||
is_const: false,
|
||||
inner: Box::new(CType::U32)
|
||||
})
|
||||
},
|
||||
"data",
|
||||
"const unsigned int** data"
|
||||
);
|
||||
// TODO: test more complicated const correctness rules: there are bugs relating to it.
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_expressions_works() {
|
||||
macro_rules! assert_c_expr {
|
||||
($cexpr:expr, $expected:expr) => {
|
||||
let mut w = String::new();
|
||||
let cexpr = $cexpr;
|
||||
cexpr.generate_c(&mut w);
|
||||
assert_eq!(w, $expected);
|
||||
};
|
||||
}
|
||||
|
||||
assert_c_expr!(
|
||||
CStatement::LiteralConstant {
|
||||
value: "\"Hello, world!\"".to_string()
|
||||
},
|
||||
"\"Hello, world!\""
|
||||
);
|
||||
assert_c_expr!(
|
||||
CStatement::TypeDef {
|
||||
source_type: CType::Function {
|
||||
arguments: vec![CType::I32, CType::I32],
|
||||
return_value: None,
|
||||
},
|
||||
new_name: "my_func_ptr".to_string(),
|
||||
},
|
||||
"typedef void (*my_func_ptr)(int, int);\n"
|
||||
);
|
||||
assert_c_expr!(
|
||||
CStatement::LiteralArray {
|
||||
items: vec![
|
||||
CStatement::LiteralConstant {
|
||||
value: "1".to_string()
|
||||
},
|
||||
CStatement::LiteralConstant {
|
||||
value: "2".to_string()
|
||||
},
|
||||
CStatement::LiteralConstant {
|
||||
value: "3".to_string()
|
||||
},
|
||||
]
|
||||
},
|
||||
"{\n\t1,\n\t2,\n\t3,\n}"
|
||||
);
|
||||
assert_c_expr!(CStatement::LiteralArray { items: vec![] }, "{}");
|
||||
assert_c_expr!(
|
||||
CStatement::Declaration {
|
||||
name: "my_array".to_string(),
|
||||
is_extern: false,
|
||||
is_const: true,
|
||||
ctype: CType::Array {
|
||||
inner: Box::new(CType::I32)
|
||||
},
|
||||
definition: Some(Box::new(CStatement::LiteralArray {
|
||||
items: vec![
|
||||
CStatement::LiteralConstant {
|
||||
value: "1".to_string()
|
||||
},
|
||||
CStatement::LiteralConstant {
|
||||
value: "2".to_string()
|
||||
},
|
||||
CStatement::LiteralConstant {
|
||||
value: "3".to_string()
|
||||
},
|
||||
]
|
||||
}))
|
||||
},
|
||||
"const int my_array[] = {\n\t1,\n\t2,\n\t3,\n};\n"
|
||||
);
|
||||
assert_c_expr!(
|
||||
CStatement::Declaration {
|
||||
name: "my_array".to_string(),
|
||||
is_extern: true,
|
||||
is_const: true,
|
||||
ctype: CType::Array {
|
||||
inner: Box::new(CType::I32)
|
||||
},
|
||||
definition: None,
|
||||
},
|
||||
"const extern int my_array[];\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,297 +0,0 @@
|
||||
//! Generate a header file for the object file produced by the Staticlib engine.
|
||||
|
||||
use super::{generate_c, CStatement, CType};
|
||||
use wasmer_compiler::{Symbol, SymbolRegistry};
|
||||
use wasmer_types::ModuleInfo;
|
||||
|
||||
/// Helper functions to simplify the usage of the Staticlib engine.
|
||||
const HELPER_FUNCTIONS: &str = r#"
|
||||
wasm_byte_vec_t generate_serialized_data() {
|
||||
// We need to pass all the bytes as one big buffer so we have to do all this logic to memcpy
|
||||
// the various pieces together from the generated header file.
|
||||
//
|
||||
// We should provide a `deseralize_vectored` function to avoid requiring this extra work.
|
||||
|
||||
char* byte_ptr = (char*)&WASMER_METADATA[0];
|
||||
|
||||
size_t num_function_pointers
|
||||
= sizeof(function_pointers) / sizeof(void*);
|
||||
size_t num_function_trampolines
|
||||
= sizeof(function_trampolines) / sizeof(void*);
|
||||
size_t num_dynamic_function_trampoline_pointers
|
||||
= sizeof(dynamic_function_trampoline_pointers) / sizeof(void*);
|
||||
|
||||
|
||||
size_t buffer_size = module_bytes_len
|
||||
+ sizeof(size_t) + sizeof(function_pointers)
|
||||
+ sizeof(size_t) + sizeof(function_trampolines)
|
||||
+ sizeof(size_t) + sizeof(dynamic_function_trampoline_pointers);
|
||||
|
||||
char* memory_buffer = (char*) malloc(buffer_size);
|
||||
size_t current_offset = 0;
|
||||
|
||||
memcpy(memory_buffer + current_offset, byte_ptr, module_bytes_len);
|
||||
current_offset += module_bytes_len;
|
||||
|
||||
memcpy(memory_buffer + current_offset, (void*)&num_function_pointers, sizeof(size_t));
|
||||
current_offset += sizeof(size_t);
|
||||
|
||||
memcpy(memory_buffer + current_offset, (void*)&function_pointers[0], sizeof(function_pointers));
|
||||
current_offset += sizeof(function_pointers);
|
||||
|
||||
memcpy(memory_buffer + current_offset, (void*)&num_function_trampolines, sizeof(size_t));
|
||||
current_offset += sizeof(size_t);
|
||||
|
||||
memcpy(memory_buffer + current_offset, (void*)&function_trampolines[0], sizeof(function_trampolines));
|
||||
current_offset += sizeof(function_trampolines);
|
||||
|
||||
memcpy(memory_buffer + current_offset, (void*)&num_dynamic_function_trampoline_pointers, sizeof(size_t));
|
||||
current_offset += sizeof(size_t);
|
||||
|
||||
memcpy(memory_buffer + current_offset, (void*)&dynamic_function_trampoline_pointers[0], sizeof(dynamic_function_trampoline_pointers));
|
||||
current_offset += sizeof(dynamic_function_trampoline_pointers);
|
||||
|
||||
wasm_byte_vec_t module_byte_vec = {
|
||||
.size = buffer_size,
|
||||
.data = memory_buffer,
|
||||
};
|
||||
return module_byte_vec;
|
||||
}
|
||||
|
||||
wasm_module_t* wasmer_staticlib_engine_new(wasm_store_t* store, const char* wasm_name) {
|
||||
// wasm_name intentionally unused for now: will be used in the future.
|
||||
wasm_byte_vec_t module_byte_vec = generate_serialized_data();
|
||||
wasm_module_t* module = wasm_module_deserialize(store, &module_byte_vec);
|
||||
free(module_byte_vec.data);
|
||||
|
||||
return module;
|
||||
}
|
||||
"#;
|
||||
|
||||
/// Generate the header file that goes with the generated object file.
|
||||
pub fn generate_header_file(
|
||||
module_info: &ModuleInfo,
|
||||
symbol_registry: &dyn SymbolRegistry,
|
||||
metadata_length: usize,
|
||||
) -> String {
|
||||
let mut c_statements = vec![
|
||||
CStatement::LiteralConstant {
|
||||
value: "#include <stdlib.h>\n#include <string.h>\n\n".to_string(),
|
||||
},
|
||||
CStatement::LiteralConstant {
|
||||
value: "#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n".to_string(),
|
||||
},
|
||||
CStatement::Declaration {
|
||||
name: "module_bytes_len".to_string(),
|
||||
is_extern: false,
|
||||
is_const: true,
|
||||
ctype: CType::U32,
|
||||
definition: Some(Box::new(CStatement::LiteralConstant {
|
||||
value: metadata_length.to_string(),
|
||||
})),
|
||||
},
|
||||
CStatement::Declaration {
|
||||
name: "WASMER_METADATA".to_string(),
|
||||
is_extern: true,
|
||||
is_const: true,
|
||||
ctype: CType::Array {
|
||||
inner: Box::new(CType::U8),
|
||||
},
|
||||
definition: None,
|
||||
},
|
||||
];
|
||||
let function_declarations = module_info
|
||||
.functions
|
||||
.iter()
|
||||
.filter_map(|(f_index, sig_index)| {
|
||||
Some((module_info.local_func_index(f_index)?, sig_index))
|
||||
})
|
||||
.map(|(function_local_index, _sig_index)| {
|
||||
let function_name =
|
||||
symbol_registry.symbol_to_name(Symbol::LocalFunction(function_local_index));
|
||||
// TODO: figure out the signature here too
|
||||
CStatement::Declaration {
|
||||
name: function_name,
|
||||
is_extern: false,
|
||||
is_const: false,
|
||||
ctype: CType::Function {
|
||||
arguments: vec![CType::Void],
|
||||
return_value: None,
|
||||
},
|
||||
definition: None,
|
||||
}
|
||||
});
|
||||
c_statements.push(CStatement::LiteralConstant {
|
||||
value: r#"
|
||||
// Compiled Wasm function pointers ordered by function index: the order they
|
||||
// appeared in in the Wasm module.
|
||||
"#
|
||||
.to_string(),
|
||||
});
|
||||
c_statements.extend(function_declarations);
|
||||
|
||||
// function pointer array
|
||||
{
|
||||
let function_pointer_array_statements = module_info
|
||||
.functions
|
||||
.iter()
|
||||
.filter_map(|(f_index, sig_index)| {
|
||||
Some((module_info.local_func_index(f_index)?, sig_index))
|
||||
})
|
||||
.map(|(function_local_index, _sig_index)| {
|
||||
let function_name =
|
||||
symbol_registry.symbol_to_name(Symbol::LocalFunction(function_local_index));
|
||||
// TODO: figure out the signature here too
|
||||
|
||||
CStatement::Cast {
|
||||
target_type: CType::void_ptr(),
|
||||
expression: Box::new(CStatement::LiteralConstant {
|
||||
value: function_name,
|
||||
}),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
c_statements.push(CStatement::Declaration {
|
||||
name: "function_pointers".to_string(),
|
||||
is_extern: false,
|
||||
is_const: true,
|
||||
ctype: CType::Array {
|
||||
inner: Box::new(CType::void_ptr()),
|
||||
},
|
||||
definition: Some(Box::new(CStatement::LiteralArray {
|
||||
items: function_pointer_array_statements,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
let func_trampoline_declarations =
|
||||
module_info
|
||||
.signatures
|
||||
.iter()
|
||||
.map(|(sig_index, _func_type)| {
|
||||
let function_name =
|
||||
symbol_registry.symbol_to_name(Symbol::FunctionCallTrampoline(sig_index));
|
||||
|
||||
CStatement::Declaration {
|
||||
name: function_name,
|
||||
is_extern: false,
|
||||
is_const: false,
|
||||
ctype: CType::Function {
|
||||
arguments: vec![CType::void_ptr(), CType::void_ptr(), CType::void_ptr()],
|
||||
return_value: None,
|
||||
},
|
||||
definition: None,
|
||||
}
|
||||
});
|
||||
c_statements.push(CStatement::LiteralConstant {
|
||||
value: r#"
|
||||
// Trampolines (functions by which we can call into Wasm) ordered by signature.
|
||||
// There is 1 trampoline per function signature in the order they appear in
|
||||
// the Wasm module.
|
||||
"#
|
||||
.to_string(),
|
||||
});
|
||||
c_statements.extend(func_trampoline_declarations);
|
||||
|
||||
// function trampolines
|
||||
{
|
||||
let function_trampoline_statements = module_info
|
||||
.signatures
|
||||
.iter()
|
||||
.map(|(sig_index, _vm_shared_index)| {
|
||||
let function_name =
|
||||
symbol_registry.symbol_to_name(Symbol::FunctionCallTrampoline(sig_index));
|
||||
CStatement::LiteralConstant {
|
||||
value: function_name,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
c_statements.push(CStatement::Declaration {
|
||||
name: "function_trampolines".to_string(),
|
||||
is_extern: false,
|
||||
is_const: true,
|
||||
ctype: CType::Array {
|
||||
inner: Box::new(CType::void_ptr()),
|
||||
},
|
||||
definition: Some(Box::new(CStatement::LiteralArray {
|
||||
items: function_trampoline_statements,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
let dyn_func_declarations = module_info
|
||||
.functions
|
||||
.keys()
|
||||
.take(module_info.num_imported_functions)
|
||||
.map(|func_index| {
|
||||
let function_name =
|
||||
symbol_registry.symbol_to_name(Symbol::DynamicFunctionTrampoline(func_index));
|
||||
// TODO: figure out the signature here
|
||||
CStatement::Declaration {
|
||||
name: function_name,
|
||||
is_extern: false,
|
||||
is_const: false,
|
||||
ctype: CType::Function {
|
||||
arguments: vec![CType::void_ptr(), CType::void_ptr(), CType::void_ptr()],
|
||||
return_value: None,
|
||||
},
|
||||
definition: None,
|
||||
}
|
||||
});
|
||||
c_statements.push(CStatement::LiteralConstant {
|
||||
value: r#"
|
||||
// Dynamic trampolines are per-function and are used for each function where
|
||||
// the type signature is not known statically. In this case, this corresponds to
|
||||
// the imported functions.
|
||||
"#
|
||||
.to_string(),
|
||||
});
|
||||
c_statements.extend(dyn_func_declarations);
|
||||
|
||||
c_statements.push(CStatement::TypeDef {
|
||||
source_type: CType::Function {
|
||||
arguments: vec![CType::void_ptr(), CType::void_ptr(), CType::void_ptr()],
|
||||
return_value: None,
|
||||
},
|
||||
new_name: "dyn_func_trampoline_t".to_string(),
|
||||
});
|
||||
|
||||
// dynamic function trampoline pointer array
|
||||
{
|
||||
let dynamic_function_trampoline_statements = module_info
|
||||
.functions
|
||||
.keys()
|
||||
.take(module_info.num_imported_functions)
|
||||
.map(|func_index| {
|
||||
let function_name =
|
||||
symbol_registry.symbol_to_name(Symbol::DynamicFunctionTrampoline(func_index));
|
||||
CStatement::LiteralConstant {
|
||||
value: function_name,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
c_statements.push(CStatement::Declaration {
|
||||
name: "dynamic_function_trampoline_pointers".to_string(),
|
||||
is_extern: false,
|
||||
is_const: true,
|
||||
ctype: CType::Array {
|
||||
inner: Box::new(CType::TypeDef("dyn_func_trampoline_t".to_string())),
|
||||
},
|
||||
definition: Some(Box::new(CStatement::LiteralArray {
|
||||
items: dynamic_function_trampoline_statements,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
c_statements.push(CStatement::LiteralConstant {
|
||||
value: HELPER_FUNCTIONS.to_string(),
|
||||
});
|
||||
|
||||
c_statements.push(CStatement::LiteralConstant {
|
||||
value: "\n#ifdef __cplusplus\n}\n#endif\n\n".to_string(),
|
||||
});
|
||||
|
||||
generate_c(&c_statements)
|
||||
}
|
||||
@@ -4,8 +4,6 @@
|
||||
use crate::commands::Binfmt;
|
||||
#[cfg(feature = "compiler")]
|
||||
use crate::commands::Compile;
|
||||
#[cfg(all(feature = "staticlib", feature = "compiler"))]
|
||||
use crate::commands::CreateExe;
|
||||
#[cfg(feature = "wast")]
|
||||
use crate::commands::Wast;
|
||||
use crate::commands::{Cache, Config, Inspect, Run, SelfUpdate, Validate};
|
||||
@@ -46,11 +44,6 @@ enum WasmerCLIOptions {
|
||||
#[structopt(name = "compile")]
|
||||
Compile(Compile),
|
||||
|
||||
/// Compile a WebAssembly binary into a native executable
|
||||
#[cfg(all(feature = "staticlib", feature = "compiler"))]
|
||||
#[structopt(name = "create-exe")]
|
||||
CreateExe(CreateExe),
|
||||
|
||||
/// Get various configuration information needed
|
||||
/// to compile programs which use Wasmer
|
||||
#[structopt(name = "config")]
|
||||
@@ -84,8 +77,6 @@ impl WasmerCLIOptions {
|
||||
Self::Validate(validate) => validate.execute(),
|
||||
#[cfg(feature = "compiler")]
|
||||
Self::Compile(compile) => compile.execute(),
|
||||
#[cfg(all(feature = "staticlib", feature = "compiler"))]
|
||||
Self::CreateExe(create_exe) => create_exe.execute(),
|
||||
Self::Config(config) => config.execute(),
|
||||
Self::Inspect(inspect) => inspect.execute(),
|
||||
#[cfg(feature = "wast")]
|
||||
|
||||
@@ -5,8 +5,6 @@ mod cache;
|
||||
#[cfg(feature = "compiler")]
|
||||
mod compile;
|
||||
mod config;
|
||||
#[cfg(all(feature = "staticlib", feature = "compiler"))]
|
||||
mod create_exe;
|
||||
mod inspect;
|
||||
mod run;
|
||||
mod self_update;
|
||||
@@ -18,8 +16,6 @@ mod wast;
|
||||
pub use binfmt::*;
|
||||
#[cfg(feature = "compiler")]
|
||||
pub use compile::*;
|
||||
#[cfg(all(feature = "staticlib", feature = "compiler"))]
|
||||
pub use create_exe::*;
|
||||
#[cfg(feature = "wast")]
|
||||
pub use wast::*;
|
||||
pub use {cache::*, config::*, inspect::*, run::*, self_update::*, validate::*};
|
||||
|
||||
@@ -16,10 +16,6 @@ pub struct Compile {
|
||||
#[structopt(name = "OUTPUT PATH", short = "o", parse(from_os_str))]
|
||||
output: PathBuf,
|
||||
|
||||
/// Output path for generated header file
|
||||
#[structopt(name = "HEADER PATH", long = "header", parse(from_os_str))]
|
||||
header_path: Option<PathBuf>,
|
||||
|
||||
/// Compilation Target triple
|
||||
#[structopt(long = "target")]
|
||||
target_triple: Option<Triple>,
|
||||
@@ -47,11 +43,7 @@ impl Compile {
|
||||
EngineType::Universal => {
|
||||
wasmer_engine_universal::UniversalArtifact::get_default_extension(target_triple)
|
||||
}
|
||||
#[cfg(feature = "staticlib")]
|
||||
EngineType::Staticlib => {
|
||||
wasmer_engine_staticlib::StaticlibArtifact::get_default_extension(target_triple)
|
||||
}
|
||||
#[cfg(not(all(feature = "universal", feature = "staticlib")))]
|
||||
#[cfg(not(all(feature = "universal")))]
|
||||
_ => bail!("selected engine type is not compiled in"),
|
||||
})
|
||||
}
|
||||
@@ -101,43 +93,6 @@ impl Compile {
|
||||
self.output.display(),
|
||||
);
|
||||
|
||||
#[cfg(feature = "staticlib")]
|
||||
if engine_type == EngineType::Staticlib {
|
||||
let artifact: &wasmer_engine_staticlib::StaticlibArtifact =
|
||||
module.artifact().as_ref().downcast_ref().context("Engine type is Staticlib but could not downcast artifact into StaticlibArtifact")?;
|
||||
let symbol_registry = artifact.symbol_registry();
|
||||
let metadata_length = artifact.metadata_length();
|
||||
let module_info = module.info();
|
||||
let header_file_src = crate::c_gen::staticlib_header::generate_header_file(
|
||||
module_info,
|
||||
symbol_registry,
|
||||
metadata_length,
|
||||
);
|
||||
|
||||
let header_path = self.header_path.as_ref().cloned().unwrap_or_else(|| {
|
||||
let mut hp = PathBuf::from(
|
||||
self.path
|
||||
.file_stem()
|
||||
.map(|fs| fs.to_string_lossy().to_string())
|
||||
.unwrap_or_else(|| "wasm_out".to_string()),
|
||||
);
|
||||
hp.set_extension("h");
|
||||
hp
|
||||
});
|
||||
// for C code
|
||||
let mut header = std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.write(true)
|
||||
.open(&header_path)?;
|
||||
|
||||
use std::io::Write;
|
||||
header.write_all(header_file_src.as_bytes())?;
|
||||
eprintln!(
|
||||
"✔ Header file generated successfully at `{}`.",
|
||||
header_path.display(),
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,316 +0,0 @@
|
||||
//! Create a standalone native executable for a given Wasm file.
|
||||
|
||||
use crate::store::{CompilerOptions, EngineType};
|
||||
use anyhow::{Context, Result};
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use structopt::StructOpt;
|
||||
use wasmer::*;
|
||||
|
||||
const WASMER_MAIN_C_SOURCE: &[u8] = include_bytes!("wasmer_create_exe_main.c");
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
/// The options for the `wasmer create-exe` subcommand
|
||||
pub struct CreateExe {
|
||||
/// Input file
|
||||
#[structopt(name = "FILE", parse(from_os_str))]
|
||||
path: PathBuf,
|
||||
|
||||
/// Output file
|
||||
#[structopt(name = "OUTPUT PATH", short = "o", parse(from_os_str))]
|
||||
output: PathBuf,
|
||||
|
||||
/// Compilation Target triple
|
||||
#[structopt(long = "target")]
|
||||
target_triple: Option<Triple>,
|
||||
|
||||
#[structopt(flatten)]
|
||||
compiler: CompilerOptions,
|
||||
|
||||
#[structopt(short = "m", multiple = true, number_of_values = 1)]
|
||||
cpu_features: Vec<CpuFeature>,
|
||||
|
||||
/// Additional libraries to link against.
|
||||
/// This is useful for fixing linker errors that may occur on some systems.
|
||||
#[structopt(short = "l", multiple = true, number_of_values = 1)]
|
||||
libraries: Vec<String>,
|
||||
}
|
||||
|
||||
impl CreateExe {
|
||||
/// Runs logic for the `compile` subcommand
|
||||
pub fn execute(&self) -> Result<()> {
|
||||
let target = self
|
||||
.target_triple
|
||||
.as_ref()
|
||||
.map(|target_triple| {
|
||||
let mut features = self
|
||||
.cpu_features
|
||||
.clone()
|
||||
.into_iter()
|
||||
.fold(CpuFeature::set(), |a, b| a | b);
|
||||
// Cranelift requires SSE2, so we have this "hack" for now to facilitate
|
||||
// usage
|
||||
features |= CpuFeature::SSE2;
|
||||
Target::new(target_triple.clone(), features)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let engine_type = EngineType::Staticlib;
|
||||
let (store, compiler_type) = self
|
||||
.compiler
|
||||
.get_store_for_target_and_engine(target.clone(), engine_type)?;
|
||||
|
||||
println!("Engine: {}", engine_type.to_string());
|
||||
println!("Compiler: {}", compiler_type.to_string());
|
||||
println!("Target: {}", target.triple());
|
||||
|
||||
let working_dir = tempfile::tempdir()?;
|
||||
let starting_cd = env::current_dir()?;
|
||||
let output_path = starting_cd.join(&self.output);
|
||||
env::set_current_dir(&working_dir)?;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
let wasm_object_path = PathBuf::from("wasm.o");
|
||||
#[cfg(windows)]
|
||||
let wasm_object_path = PathBuf::from("wasm.obj");
|
||||
|
||||
let wasm_module_path = starting_cd.join(&self.path);
|
||||
|
||||
let module =
|
||||
Module::from_file(&store, &wasm_module_path).context("failed to compile Wasm")?;
|
||||
let _ = module.serialize_to_file(&wasm_object_path)?;
|
||||
|
||||
let artifact: &wasmer_engine_staticlib::StaticlibArtifact =
|
||||
module.artifact().as_ref().downcast_ref().context(
|
||||
"Engine type is Staticlib but could not downcast artifact into StaticlibArtifact",
|
||||
)?;
|
||||
let symbol_registry = artifact.symbol_registry();
|
||||
let metadata_length = artifact.metadata_length();
|
||||
let module_info = module.info();
|
||||
let header_file_src = crate::c_gen::staticlib_header::generate_header_file(
|
||||
module_info,
|
||||
symbol_registry,
|
||||
metadata_length,
|
||||
);
|
||||
|
||||
generate_header(header_file_src.as_bytes())?;
|
||||
self.compile_c(wasm_object_path, output_path)?;
|
||||
|
||||
eprintln!(
|
||||
"✔ Native executable compiled successfully to `{}`.",
|
||||
self.output.display(),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compile_c(&self, wasm_object_path: PathBuf, output_path: PathBuf) -> anyhow::Result<()> {
|
||||
use std::io::Write;
|
||||
|
||||
// write C src to disk
|
||||
let c_src_path = Path::new("wasmer_main.c");
|
||||
#[cfg(not(windows))]
|
||||
let c_src_obj = PathBuf::from("wasmer_main.o");
|
||||
#[cfg(windows)]
|
||||
let c_src_obj = PathBuf::from("wasmer_main.obj");
|
||||
|
||||
{
|
||||
let mut c_src_file = fs::OpenOptions::new()
|
||||
.create_new(true)
|
||||
.write(true)
|
||||
.open(&c_src_path)
|
||||
.context("Failed to open C source code file")?;
|
||||
c_src_file.write_all(WASMER_MAIN_C_SOURCE)?;
|
||||
}
|
||||
run_c_compile(c_src_path, &c_src_obj, self.target_triple.clone())
|
||||
.context("Failed to compile C source code")?;
|
||||
LinkCode {
|
||||
object_paths: vec![c_src_obj, wasm_object_path],
|
||||
output_path,
|
||||
additional_libraries: self.libraries.clone(),
|
||||
target: self.target_triple.clone(),
|
||||
..Default::default()
|
||||
}
|
||||
.run()
|
||||
.context("Failed to link objects together")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_header(header_file_src: &[u8]) -> anyhow::Result<()> {
|
||||
let header_file_path = Path::new("my_wasm.h");
|
||||
let mut header = std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.write(true)
|
||||
.open(&header_file_path)?;
|
||||
|
||||
use std::io::Write;
|
||||
header.write_all(header_file_src)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_wasmer_dir() -> anyhow::Result<PathBuf> {
|
||||
Ok(PathBuf::from(
|
||||
env::var("WASMER_DIR")
|
||||
.or_else(|e| {
|
||||
option_env!("WASMER_INSTALL_PREFIX")
|
||||
.map(str::to_string)
|
||||
.ok_or(e)
|
||||
})
|
||||
.context("Trying to read env var `WASMER_DIR`")?,
|
||||
))
|
||||
}
|
||||
|
||||
fn get_wasmer_include_directory() -> anyhow::Result<PathBuf> {
|
||||
let mut path = get_wasmer_dir()?;
|
||||
path.push("include");
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
/// path to the static libwasmer
|
||||
fn get_libwasmer_path() -> anyhow::Result<PathBuf> {
|
||||
let mut path = get_wasmer_dir()?;
|
||||
path.push("lib");
|
||||
|
||||
// TODO: prefer headless Wasmer if/when it's a separate library.
|
||||
#[cfg(not(windows))]
|
||||
path.push("libwasmer.a");
|
||||
#[cfg(windows)]
|
||||
path.push("wasmer.lib");
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
/// Compile the C code.
|
||||
fn run_c_compile(
|
||||
path_to_c_src: &Path,
|
||||
output_name: &Path,
|
||||
target: Option<Triple>,
|
||||
) -> anyhow::Result<()> {
|
||||
#[cfg(not(windows))]
|
||||
let c_compiler = "cc";
|
||||
// We must use a C++ compiler on Windows because wasm.h uses `static_assert`
|
||||
// which isn't available in `clang` on Windows.
|
||||
#[cfg(windows)]
|
||||
let c_compiler = "clang++";
|
||||
|
||||
let mut command = Command::new(c_compiler);
|
||||
let command = command
|
||||
.arg("-O2")
|
||||
.arg("-c")
|
||||
.arg(path_to_c_src)
|
||||
.arg("-I")
|
||||
.arg(get_wasmer_include_directory()?);
|
||||
|
||||
let command = if let Some(target) = target {
|
||||
command.arg("-target").arg(format!("{}", target))
|
||||
} else {
|
||||
command
|
||||
};
|
||||
|
||||
let output = command.arg("-o").arg(output_name).output()?;
|
||||
|
||||
if !output.status.success() {
|
||||
bail!(
|
||||
"C code compile failed with: stdout: {}\n\nstderr: {}",
|
||||
std::str::from_utf8(&output.stdout)
|
||||
.expect("stdout is not utf8! need to handle arbitrary bytes"),
|
||||
std::str::from_utf8(&output.stderr)
|
||||
.expect("stderr is not utf8! need to handle arbitrary bytes")
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Data used to run a linking command for generated artifacts.
|
||||
#[derive(Debug)]
|
||||
struct LinkCode {
|
||||
/// Path to the linker used to run the linking command.
|
||||
linker_path: PathBuf,
|
||||
/// String used as an optimization flag.
|
||||
optimization_flag: String,
|
||||
/// Paths of objects to link.
|
||||
object_paths: Vec<PathBuf>,
|
||||
/// Additional libraries to link against.
|
||||
additional_libraries: Vec<String>,
|
||||
/// Path to the output target.
|
||||
output_path: PathBuf,
|
||||
/// Path to the dir containing the static libwasmer library.
|
||||
libwasmer_path: PathBuf,
|
||||
/// The target to link the executable for.
|
||||
target: Option<Triple>,
|
||||
}
|
||||
|
||||
impl Default for LinkCode {
|
||||
fn default() -> Self {
|
||||
#[cfg(not(windows))]
|
||||
let linker = "cc";
|
||||
#[cfg(windows)]
|
||||
let linker = "clang";
|
||||
Self {
|
||||
linker_path: PathBuf::from(linker),
|
||||
optimization_flag: String::from("-O2"),
|
||||
object_paths: vec![],
|
||||
additional_libraries: vec![],
|
||||
output_path: PathBuf::from("a.out"),
|
||||
libwasmer_path: get_libwasmer_path().unwrap(),
|
||||
target: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LinkCode {
|
||||
fn run(&self) -> anyhow::Result<()> {
|
||||
let mut command = Command::new(&self.linker_path);
|
||||
let command = command
|
||||
.arg(&self.optimization_flag)
|
||||
.args(
|
||||
self.object_paths
|
||||
.iter()
|
||||
.map(|path| path.canonicalize().unwrap()),
|
||||
)
|
||||
.arg(
|
||||
&self
|
||||
.libwasmer_path
|
||||
.canonicalize()
|
||||
.context("Failed to find libwasmer")?,
|
||||
);
|
||||
let command = if let Some(target) = &self.target {
|
||||
command.arg("-target").arg(format!("{}", target))
|
||||
} else {
|
||||
command
|
||||
};
|
||||
// Add libraries required per platform.
|
||||
// We need userenv, sockets (Ws2_32), advapi32 for some system calls and bcrypt for random numbers.
|
||||
#[cfg(windows)]
|
||||
let command = command
|
||||
.arg("-luserenv")
|
||||
.arg("-lWs2_32")
|
||||
.arg("-ladvapi32")
|
||||
.arg("-lbcrypt");
|
||||
// On unix we need dlopen-related symbols, libmath for a few things, and pthreads.
|
||||
#[cfg(not(windows))]
|
||||
let command = command.arg("-ldl").arg("-lm").arg("-pthread");
|
||||
let link_aganist_extra_libs = self
|
||||
.additional_libraries
|
||||
.iter()
|
||||
.map(|lib| format!("-l{}", lib));
|
||||
let command = command.args(link_aganist_extra_libs);
|
||||
let output = command.arg("-o").arg(&self.output_path).output()?;
|
||||
|
||||
if !output.status.success() {
|
||||
bail!(
|
||||
"linking failed with: stdout: {}\n\nstderr: {}",
|
||||
std::str::from_utf8(&output.stdout)
|
||||
.expect("stdout is not utf8! need to handle arbitrary bytes"),
|
||||
std::str::from_utf8(&output.stderr)
|
||||
.expect("stderr is not utf8! need to handle arbitrary bytes")
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
#include "wasmer.h"
|
||||
#include "my_wasm.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define own
|
||||
|
||||
// TODO: make this define templated so that the Rust code can toggle it on/off
|
||||
#define WASI
|
||||
|
||||
static void print_wasmer_error() {
|
||||
int error_len = wasmer_last_error_length();
|
||||
if (error_len > 0) {
|
||||
printf("Error len: `%d`\n", error_len);
|
||||
char *error_str = (char *)malloc(error_len);
|
||||
wasmer_last_error_message(error_str, error_len);
|
||||
printf("%s\n", error_str);
|
||||
free(error_str);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WASI
|
||||
static void pass_mapdir_arg(wasi_config_t *wasi_config, char *mapdir) {
|
||||
int colon_location = strchr(mapdir, ':') - mapdir;
|
||||
if (colon_location == 0) {
|
||||
// error malformed argument
|
||||
fprintf(stderr, "Expected mapdir argument of the form alias:directory\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
char *alias = (char *)malloc(colon_location + 1);
|
||||
memcpy(alias, mapdir, colon_location);
|
||||
alias[colon_location] = '\0';
|
||||
|
||||
int dir_len = strlen(mapdir) - colon_location;
|
||||
char *dir = (char *)malloc(dir_len + 1);
|
||||
memcpy(dir, &mapdir[colon_location + 1], dir_len);
|
||||
dir[dir_len] = '\0';
|
||||
|
||||
wasi_config_mapdir(wasi_config, alias, dir);
|
||||
free(alias);
|
||||
free(dir);
|
||||
}
|
||||
|
||||
// We try to parse out `--dir` and `--mapdir` ahead of time and process those
|
||||
// specially. All other arguments are passed to the guest program.
|
||||
static void handle_arguments(wasi_config_t *wasi_config, int argc,
|
||||
char *argv[]) {
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
// We probably want special args like `--dir` and `--mapdir` to not be
|
||||
// passed directly
|
||||
if (strcmp(argv[i], "--dir") == 0) {
|
||||
// next arg is a preopen directory
|
||||
if ((i + 1) < argc) {
|
||||
i++;
|
||||
wasi_config_preopen_dir(wasi_config, argv[i]);
|
||||
} else {
|
||||
fprintf(stderr, "--dir expects a following argument specifying which "
|
||||
"directory to preopen\n");
|
||||
exit(-1);
|
||||
}
|
||||
} else if (strcmp(argv[i], "--mapdir") == 0) {
|
||||
// next arg is a mapdir
|
||||
if ((i + 1) < argc) {
|
||||
i++;
|
||||
pass_mapdir_arg(wasi_config, argv[i]);
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"--mapdir expects a following argument specifying which "
|
||||
"directory to preopen in the form alias:directory\n");
|
||||
exit(-1);
|
||||
}
|
||||
} else if (strncmp(argv[i], "--dir=", strlen("--dir=")) == 0) {
|
||||
// this arg is a preopen dir
|
||||
char *dir = argv[i] + strlen("--dir=");
|
||||
wasi_config_preopen_dir(wasi_config, dir);
|
||||
} else if (strncmp(argv[i], "--mapdir=", strlen("--mapdir=")) == 0) {
|
||||
// this arg is a mapdir
|
||||
char *mapdir = argv[i] + strlen("--mapdir=");
|
||||
pass_mapdir_arg(wasi_config, mapdir);
|
||||
} else {
|
||||
// guest argument
|
||||
wasi_config_arg(wasi_config, argv[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
wasm_config_t *config = wasm_config_new();
|
||||
wasm_config_set_engine(config, STATICLIB);
|
||||
wasm_engine_t *engine = wasm_engine_new_with_config(config);
|
||||
wasm_store_t *store = wasm_store_new(engine);
|
||||
|
||||
wasm_module_t *module = wasmer_staticlib_engine_new(store, argv[0]);
|
||||
|
||||
if (!module) {
|
||||
fprintf(stderr, "Failed to create module\n");
|
||||
print_wasmer_error();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// We have now finished the memory buffer book keeping and we have a valid
|
||||
// Module.
|
||||
|
||||
#ifdef WASI
|
||||
wasi_config_t *wasi_config = wasi_config_new(argv[0]);
|
||||
handle_arguments(wasi_config, argc, argv);
|
||||
|
||||
wasi_env_t *wasi_env = wasi_env_new(wasi_config);
|
||||
if (!wasi_env) {
|
||||
fprintf(stderr, "Error building WASI env!\n");
|
||||
print_wasmer_error();
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
wasm_importtype_vec_t import_types;
|
||||
wasm_module_imports(module, &import_types);
|
||||
|
||||
wasm_extern_vec_t imports;
|
||||
wasm_extern_vec_new_uninitialized(&imports, import_types.size);
|
||||
wasm_importtype_vec_delete(&import_types);
|
||||
|
||||
#ifdef WASI
|
||||
bool get_imports_result = wasi_get_imports(store, module, wasi_env, &imports);
|
||||
wasi_env_delete(wasi_env);
|
||||
|
||||
if (!get_imports_result) {
|
||||
fprintf(stderr, "Error getting WASI imports!\n");
|
||||
print_wasmer_error();
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
wasm_instance_t *instance = wasm_instance_new(store, module, &imports, NULL);
|
||||
|
||||
if (!instance) {
|
||||
fprintf(stderr, "Failed to create instance\n");
|
||||
print_wasmer_error();
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef WASI
|
||||
own wasm_func_t *start_function = wasi_get_start_function(instance);
|
||||
if (!start_function) {
|
||||
fprintf(stderr, "`_start` function not found\n");
|
||||
print_wasmer_error();
|
||||
return -1;
|
||||
}
|
||||
|
||||
wasm_val_vec_t args = WASM_EMPTY_VEC;
|
||||
wasm_val_vec_t results = WASM_EMPTY_VEC;
|
||||
own wasm_trap_t *trap = wasm_func_call(start_function, &args, &results);
|
||||
if (trap) {
|
||||
fprintf(stderr, "Trap is not NULL: TODO:\n");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// TODO: handle non-WASI start (maybe with invoke?)
|
||||
|
||||
wasm_instance_delete(instance);
|
||||
wasm_module_delete(module);
|
||||
wasm_store_delete(store);
|
||||
wasm_engine_delete(engine);
|
||||
return 0;
|
||||
}
|
||||
@@ -20,7 +20,6 @@ pub mod commands;
|
||||
pub mod common;
|
||||
#[macro_use]
|
||||
pub mod error;
|
||||
pub mod c_gen;
|
||||
pub mod cli;
|
||||
#[cfg(feature = "debug")]
|
||||
pub mod logging;
|
||||
|
||||
@@ -23,20 +23,12 @@ pub struct StoreOptions {
|
||||
compiler: CompilerOptions,
|
||||
|
||||
/// Use the Universal Engine.
|
||||
#[structopt(long, conflicts_with_all = &["staticlib", "jit", "object_file"])]
|
||||
#[structopt(long, conflicts_with_all = &["jit"])]
|
||||
universal: bool,
|
||||
|
||||
/// Use the Staticlib Engine.
|
||||
#[structopt(long, conflicts_with_all = &["universal", "jit", "object_file"])]
|
||||
staticlib: bool,
|
||||
|
||||
/// Use the JIT (Universal) Engine.
|
||||
#[structopt(long, hidden = true, conflicts_with_all = &["universal", "staticlib", "object_file"])]
|
||||
#[structopt(long, hidden = true, conflicts_with_all = &["universal"])]
|
||||
jit: bool,
|
||||
|
||||
/// Use the ObjectFile (Staticlib) Engine.
|
||||
#[structopt(long, hidden = true, conflicts_with_all = &["universal", "staticlib", "jit"])]
|
||||
object_file: bool,
|
||||
}
|
||||
|
||||
#[cfg(feature = "compiler")]
|
||||
@@ -142,14 +134,7 @@ impl CompilerOptions {
|
||||
.target(target)
|
||||
.engine(),
|
||||
),
|
||||
#[cfg(feature = "staticlib")]
|
||||
EngineType::Staticlib => Box::new(
|
||||
wasmer_engine_staticlib::Staticlib::new(compiler_config)
|
||||
.target(target)
|
||||
.features(features)
|
||||
.engine(),
|
||||
),
|
||||
#[cfg(not(all(feature = "universal", feature = "staticlib")))]
|
||||
#[cfg(not(all(feature = "universal")))]
|
||||
engine => bail!(
|
||||
"The `{}` engine is not included in this binary.",
|
||||
engine.to_string()
|
||||
@@ -341,15 +326,12 @@ impl ToString for CompilerType {
|
||||
pub enum EngineType {
|
||||
/// Universal Engine
|
||||
Universal,
|
||||
/// Static Engine
|
||||
Staticlib,
|
||||
}
|
||||
|
||||
impl ToString for EngineType {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Self::Universal => "universal".to_string(),
|
||||
Self::Staticlib => "staticlib".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -392,14 +374,10 @@ impl StoreOptions {
|
||||
fn get_engine(&self) -> Result<EngineType> {
|
||||
if self.universal || self.jit {
|
||||
Ok(EngineType::Universal)
|
||||
} else if self.staticlib || self.object_file {
|
||||
Ok(EngineType::Staticlib)
|
||||
} else {
|
||||
// Auto mode, we choose the best engine for that platform
|
||||
if cfg!(feature = "universal") {
|
||||
Ok(EngineType::Universal)
|
||||
} else if cfg!(feature = "staticlib") {
|
||||
Ok(EngineType::Staticlib)
|
||||
} else {
|
||||
bail!("There are no available engines for your architecture")
|
||||
}
|
||||
@@ -417,11 +395,7 @@ impl StoreOptions {
|
||||
EngineType::Universal => {
|
||||
Arc::new(wasmer_engine_universal::Universal::headless().engine())
|
||||
}
|
||||
#[cfg(feature = "staticlib")]
|
||||
EngineType::Staticlib => {
|
||||
Arc::new(wasmer_engine_staticlib::Staticlib::headless().engine())
|
||||
}
|
||||
#[cfg(not(all(feature = "universal", feature = "staticlib")))]
|
||||
#[cfg(not(all(feature = "universal")))]
|
||||
engine => bail!(
|
||||
"The `{}` engine is not included in this binary.",
|
||||
engine.to_string()
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
[package]
|
||||
name = "wasmer-engine-staticlib"
|
||||
version = "2.3.0"
|
||||
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
description = "Wasmer Staticlib Engine"
|
||||
categories = ["wasm"]
|
||||
keywords = ["webassembly", "wasm"]
|
||||
repository = "https://github.com/wasmerio/wasmer"
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
wasmer-artifact = { path = "../artifact", version = "=2.3.0" }
|
||||
wasmer-types = { path = "../types", version = "=2.3.0" }
|
||||
wasmer-compiler = { path = "../compiler", version = "=2.3.0" }
|
||||
wasmer-vm = { path = "../vm", version = "=2.3.0" }
|
||||
wasmer-engine = { path = "../engine", version = "=2.3.0" }
|
||||
wasmer-object = { path = "../object", version = "=2.3.0" }
|
||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
||||
cfg-if = "1.0"
|
||||
tracing = "0.1"
|
||||
bincode = "1.3"
|
||||
leb128 = "0.2"
|
||||
libloading = "0.7"
|
||||
tempfile = "3.1"
|
||||
enumset = "1.0"
|
||||
|
||||
[features]
|
||||
# Enable the `compiler` feature if you want the engine to compile
|
||||
# and not be only on headless mode.
|
||||
compiler = ["wasmer-compiler/translator"]
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
@@ -1,139 +0,0 @@
|
||||
# Wasmer Engine Staticlib
|
||||
|
||||
This is an [engine](https://crates.io/crates/wasmer-engine) for the
|
||||
[wasmer](https://crates.io/crates/wasmer) WebAssembly VM.
|
||||
|
||||
This engine is used to produce a native static object library that can
|
||||
be linked against providing a sandboxed WebAssembly runtime
|
||||
environment for the compiled module with no need for runtime
|
||||
compilation.
|
||||
|
||||
## Example of use
|
||||
|
||||
First we compile our WebAssembly file with Wasmer
|
||||
|
||||
```sh
|
||||
wasmer compile path/to/wasm/file.wasm --llvm --staticlib -o my_wasm.o --header my_wasm.h
|
||||
```
|
||||
|
||||
You will then see output like:
|
||||
|
||||
```
|
||||
Engine: staticlib
|
||||
Compiler: llvm
|
||||
Target: x86_64-apple-darwin
|
||||
✔ File compiled successfully to `my_wasm.o`.
|
||||
✔ Header file generated successfully at `my_wasm.h`.
|
||||
```
|
||||
|
||||
Now let's create a program to link with this static object file.
|
||||
|
||||
```c
|
||||
#include "wasmer.h"
|
||||
#include "my_wasm.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define own
|
||||
|
||||
static void print_wasmer_error()
|
||||
{
|
||||
int error_len = wasmer_last_error_length();
|
||||
if (error_len > 0) {
|
||||
printf("Error len: `%d`\n", error_len);
|
||||
char* error_str = (char*) malloc(error_len);
|
||||
wasmer_last_error_message(error_str, error_len);
|
||||
printf("Error str: `%s`\n", error_str);
|
||||
free(error_str);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("Initializing...\n");
|
||||
wasm_config_t* config = wasm_config_new();
|
||||
wasm_config_set_engine(config, STATICLIB);
|
||||
wasm_engine_t* engine = wasm_engine_new_with_config(config);
|
||||
wasm_store_t* store = wasm_store_new(engine);
|
||||
|
||||
wasm_module_t* module = wasmer_staticlib_engine_new(store, "qjs.wasm");
|
||||
if (!module) {
|
||||
printf("Failed to create module\n");
|
||||
print_wasmer_error();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// We have now finished the memory buffer book keeping and we have a valid Module.
|
||||
|
||||
// In this example we're passing some JavaScript source code as a command line argument
|
||||
// to a WASI module that can evaluate JavaScript.
|
||||
wasi_config_t* wasi_config = wasi_config_new("constant_value_here");
|
||||
const char* js_string = "function greet(name) { return JSON.stringify('Hello, ' + name); }; print(greet('World'));";
|
||||
wasi_config_arg(wasi_config, "--eval");
|
||||
wasi_config_arg(wasi_config, js_string);
|
||||
wasi_env_t* wasi_env = wasi_env_new(wasi_config);
|
||||
if (!wasi_env) {
|
||||
printf("> Error building WASI env!\n");
|
||||
print_wasmer_error();
|
||||
return 1;
|
||||
}
|
||||
|
||||
wasm_importtype_vec_t import_types;
|
||||
wasm_module_imports(module, &import_types);
|
||||
int num_imports = import_types.size;
|
||||
wasm_extern_t** imports = (wasm_extern_t**) malloc(num_imports * sizeof(wasm_extern_t*));
|
||||
wasm_importtype_vec_delete(&import_types);
|
||||
|
||||
bool get_imports_result = wasi_get_imports(store, module, wasi_env, imports);
|
||||
wasi_env_delete(wasi_env);
|
||||
if (!get_imports_result) {
|
||||
printf("> Error getting WASI imports!\n");
|
||||
print_wasmer_error();
|
||||
return 1;
|
||||
}
|
||||
|
||||
wasm_instance_t* instance = wasm_instance_new(store, module, (const wasm_extern_t* const*) imports, NULL);
|
||||
if (! instance) {
|
||||
printf("Failed to create instance\n");
|
||||
print_wasmer_error();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// WASI is now set up.
|
||||
own wasm_func_t* start_function = wasi_get_start_function(instance);
|
||||
if (!start_function) {
|
||||
fprintf(stderr, "`_start` function not found\n");
|
||||
print_wasmer_error();
|
||||
return -1;
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
own wasm_trap_t* trap = wasm_func_call(start_function, NULL, NULL);
|
||||
if (trap) {
|
||||
fprintf(stderr, "Trap is not NULL: TODO:\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
wasm_instance_delete(instance);
|
||||
wasm_module_delete(module);
|
||||
wasm_store_delete(store);
|
||||
wasm_engine_delete(engine);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
We save that source code into `test.c` and run:
|
||||
|
||||
```sh
|
||||
clang -O2 -c test.c -o test.o
|
||||
```
|
||||
|
||||
Now we just need to link everything together:
|
||||
|
||||
```sh
|
||||
clang -O2 test.o my_wasm.o libwasmer.a
|
||||
```
|
||||
|
||||
We link the static object file we created with our C code, the object
|
||||
file we generated with Wasmer, and `libwasmer` together and produce an
|
||||
executable that can call into our compiled WebAssembly!
|
||||
@@ -1,509 +0,0 @@
|
||||
//! Define `StaticlibArtifact` to allow compiling and instantiating to be
|
||||
//! done as separate steps.
|
||||
|
||||
use crate::engine::{StaticlibEngine, StaticlibEngineInner};
|
||||
use crate::serialize::{ModuleMetadata, ModuleMetadataSymbolRegistry};
|
||||
use enumset::EnumSet;
|
||||
use std::collections::BTreeMap;
|
||||
use std::error::Error;
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
use wasmer_artifact::ArtifactCreate;
|
||||
use wasmer_compiler::{
|
||||
CompileError, CpuFeature, Features, OperatingSystem, SymbolRegistry, Triple,
|
||||
};
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_compiler::{
|
||||
CompileModuleInfo, Compiler, FunctionBodyData, ModuleEnvironment, ModuleMiddlewareChain,
|
||||
ModuleTranslationState,
|
||||
};
|
||||
use wasmer_engine::{
|
||||
Artifact, DeserializeError, InstantiationError, MetadataHeader, SerializeError,
|
||||
};
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_engine::{Engine, Tunables};
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_object::{emit_compilation, emit_data, get_object_for_target};
|
||||
use wasmer_types::entity::EntityRef;
|
||||
use wasmer_types::entity::{BoxedSlice, PrimaryMap};
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_types::DataInitializer;
|
||||
use wasmer_types::{
|
||||
FunctionIndex, LocalFunctionIndex, MemoryIndex, ModuleInfo, OwnedDataInitializer,
|
||||
SignatureIndex, TableIndex,
|
||||
};
|
||||
use wasmer_vm::{
|
||||
FuncDataRegistry, FunctionBodyPtr, MemoryStyle, TableStyle, VMSharedSignatureIndex,
|
||||
VMTrampoline,
|
||||
};
|
||||
|
||||
/// A compiled wasm module, ready to be instantiated.
|
||||
pub struct StaticlibArtifact {
|
||||
metadata: ModuleMetadata,
|
||||
module_bytes: Vec<u8>,
|
||||
finished_functions: BoxedSlice<LocalFunctionIndex, FunctionBodyPtr>,
|
||||
finished_function_call_trampolines: BoxedSlice<SignatureIndex, VMTrampoline>,
|
||||
finished_dynamic_function_trampolines: BoxedSlice<FunctionIndex, FunctionBodyPtr>,
|
||||
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
|
||||
func_data_registry: Arc<FuncDataRegistry>,
|
||||
/// Length of the serialized metadata
|
||||
metadata_length: usize,
|
||||
symbol_registry: ModuleMetadataSymbolRegistry,
|
||||
is_compiled: bool,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn to_compile_error(err: impl Error) -> CompileError {
|
||||
CompileError::Codegen(format!("{}", err))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
const WASMER_METADATA_SYMBOL: &[u8] = b"WASMER_METADATA";
|
||||
|
||||
impl StaticlibArtifact {
|
||||
// Mach-O header in Mac
|
||||
#[allow(dead_code)]
|
||||
const MAGIC_HEADER_MH_CIGAM_64: &'static [u8] = &[207, 250, 237, 254];
|
||||
|
||||
// ELF Magic header for Linux (32 bit)
|
||||
#[allow(dead_code)]
|
||||
const MAGIC_HEADER_ELF_32: &'static [u8] = &[0x7f, b'E', b'L', b'F', 1];
|
||||
|
||||
// ELF Magic header for Linux (64 bit)
|
||||
#[allow(dead_code)]
|
||||
const MAGIC_HEADER_ELF_64: &'static [u8] = &[0x7f, b'E', b'L', b'F', 2];
|
||||
|
||||
// COFF Magic header for Windows (64 bit)
|
||||
#[allow(dead_code)]
|
||||
const MAGIC_HEADER_COFF_64: &'static [u8] = &[b'M', b'Z'];
|
||||
|
||||
/// Check if the provided bytes look like `StaticlibArtifact`.
|
||||
///
|
||||
/// This means, if the bytes look like a static object file in the
|
||||
/// target system.
|
||||
pub fn is_deserializable(bytes: &[u8]) -> bool {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(target_pointer_width = "64", target_vendor="apple"))] {
|
||||
bytes.starts_with(Self::MAGIC_HEADER_MH_CIGAM_64)
|
||||
}
|
||||
else if #[cfg(all(target_pointer_width = "64", target_os="linux"))] {
|
||||
bytes.starts_with(Self::MAGIC_HEADER_ELF_64)
|
||||
}
|
||||
else if #[cfg(all(target_pointer_width = "32", target_os="linux"))] {
|
||||
bytes.starts_with(Self::MAGIC_HEADER_ELF_32)
|
||||
}
|
||||
else if #[cfg(all(target_pointer_width = "64", target_os="windows"))] {
|
||||
bytes.starts_with(Self::MAGIC_HEADER_COFF_64)
|
||||
}
|
||||
else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "compiler")]
|
||||
#[allow(clippy::type_complexity)]
|
||||
/// Generate a compilation
|
||||
fn generate_metadata<'data>(
|
||||
data: &'data [u8],
|
||||
features: &Features,
|
||||
compiler: &dyn Compiler,
|
||||
tunables: &dyn Tunables,
|
||||
) -> Result<
|
||||
(
|
||||
CompileModuleInfo,
|
||||
PrimaryMap<LocalFunctionIndex, FunctionBodyData<'data>>,
|
||||
Vec<DataInitializer<'data>>,
|
||||
Option<ModuleTranslationState>,
|
||||
),
|
||||
CompileError,
|
||||
> {
|
||||
let environ = ModuleEnvironment::new();
|
||||
let translation = environ.translate(data).map_err(CompileError::Wasm)?;
|
||||
|
||||
// We try to apply the middleware first
|
||||
let mut module = translation.module;
|
||||
let middlewares = compiler.get_middlewares();
|
||||
middlewares.apply_on_module_info(&mut module);
|
||||
|
||||
let memory_styles: PrimaryMap<MemoryIndex, MemoryStyle> = module
|
||||
.memories
|
||||
.values()
|
||||
.map(|memory_type| tunables.memory_style(memory_type))
|
||||
.collect();
|
||||
let table_styles: PrimaryMap<TableIndex, TableStyle> = module
|
||||
.tables
|
||||
.values()
|
||||
.map(|table_type| tunables.table_style(table_type))
|
||||
.collect();
|
||||
|
||||
let compile_info = CompileModuleInfo {
|
||||
module: Arc::new(module),
|
||||
features: features.clone(),
|
||||
memory_styles,
|
||||
table_styles,
|
||||
};
|
||||
Ok((
|
||||
compile_info,
|
||||
translation.function_body_inputs,
|
||||
translation.data_initializers,
|
||||
translation.module_translation_state,
|
||||
))
|
||||
}
|
||||
|
||||
/// Compile a data buffer into a `StaticlibArtifact`, which can be statically linked against
|
||||
/// and run later.
|
||||
#[cfg(feature = "compiler")]
|
||||
pub fn new(
|
||||
engine: &StaticlibEngine,
|
||||
data: &[u8],
|
||||
tunables: &dyn Tunables,
|
||||
) -> Result<Self, CompileError> {
|
||||
let mut engine_inner = engine.inner_mut();
|
||||
let target = engine.target();
|
||||
let compiler = engine_inner.compiler()?;
|
||||
let (compile_info, function_body_inputs, data_initializers, module_translation) =
|
||||
Self::generate_metadata(data, engine_inner.features(), compiler, tunables)?;
|
||||
|
||||
let data_initializers = data_initializers
|
||||
.iter()
|
||||
.map(OwnedDataInitializer::new)
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice();
|
||||
|
||||
let target_triple = target.triple();
|
||||
|
||||
// TODO: we currently supply all-zero function body lengths.
|
||||
// We don't know the lengths until they're compiled, yet we have to
|
||||
// supply the metadata as an input to the compile.
|
||||
let function_body_lengths = function_body_inputs
|
||||
.keys()
|
||||
.map(|_function_body| 0u64)
|
||||
.collect::<PrimaryMap<LocalFunctionIndex, u64>>();
|
||||
|
||||
let mut metadata = ModuleMetadata {
|
||||
compile_info,
|
||||
prefix: engine_inner.get_prefix(data),
|
||||
data_initializers,
|
||||
function_body_lengths,
|
||||
cpu_features: target.cpu_features().as_u64(),
|
||||
};
|
||||
|
||||
/*
|
||||
In the C file we need:
|
||||
- imports
|
||||
- exports
|
||||
|
||||
to construct an api::Module which is a Store (can be passed in via argument) and an
|
||||
Arc<dyn Artifact> which means this struct which includes:
|
||||
- CompileModuleInfo
|
||||
- Features
|
||||
- ModuleInfo
|
||||
- MemoryIndex -> MemoryStyle
|
||||
- TableIndex -> TableStyle
|
||||
- LocalFunctionIndex -> FunctionBodyPtr // finished functions
|
||||
- FunctionIndex -> FunctionBodyPtr // finished dynamic function trampolines
|
||||
- SignatureIndex -> VMSharedSignatureindextureIndex // signatures
|
||||
*/
|
||||
|
||||
let serialized_data = bincode::serialize(&metadata).map_err(to_compile_error)?;
|
||||
let mut metadata_binary = vec![];
|
||||
metadata_binary.extend(MetadataHeader::new(serialized_data.len()).into_bytes());
|
||||
metadata_binary.extend(serialized_data);
|
||||
let metadata_length = metadata_binary.len();
|
||||
|
||||
let (compile_info, symbol_registry) = metadata.split();
|
||||
|
||||
let mut module = (*compile_info.module).clone();
|
||||
let middlewares = compiler.get_middlewares();
|
||||
middlewares.apply_on_module_info(&mut module);
|
||||
compile_info.module = Arc::new(module);
|
||||
|
||||
let maybe_obj_bytes = compiler.experimental_native_compile_module(
|
||||
target,
|
||||
compile_info,
|
||||
module_translation.as_ref().unwrap(),
|
||||
&function_body_inputs,
|
||||
&symbol_registry,
|
||||
&metadata_binary,
|
||||
);
|
||||
|
||||
let obj_bytes = if let Some(obj_bytes) = maybe_obj_bytes {
|
||||
obj_bytes?
|
||||
} else {
|
||||
let compilation = compiler.compile_module(
|
||||
target,
|
||||
&metadata.compile_info,
|
||||
module_translation.as_ref().unwrap(),
|
||||
function_body_inputs,
|
||||
)?;
|
||||
// there's an ordering issue, but we can update function_body_lengths here.
|
||||
/*
|
||||
// We construct the function body lengths
|
||||
let function_body_lengths = compilation
|
||||
.get_function_bodies()
|
||||
.values()
|
||||
.map(|function_body| function_body.body.len() as u64)
|
||||
.collect::<PrimaryMap<LocalFunctionIndex, u64>>();
|
||||
*/
|
||||
let mut obj = get_object_for_target(target_triple).map_err(to_compile_error)?;
|
||||
emit_data(&mut obj, WASMER_METADATA_SYMBOL, &metadata_binary, 1)
|
||||
.map_err(to_compile_error)?;
|
||||
emit_compilation(&mut obj, compilation, &symbol_registry, target_triple)
|
||||
.map_err(to_compile_error)?;
|
||||
obj.write().map_err(to_compile_error)?
|
||||
};
|
||||
|
||||
Self::from_parts_crosscompiled(&mut *engine_inner, metadata, obj_bytes, metadata_length)
|
||||
}
|
||||
|
||||
/// Get the default extension when serializing this artifact
|
||||
pub fn get_default_extension(triple: &Triple) -> &'static str {
|
||||
match triple.operating_system {
|
||||
OperatingSystem::Windows => "obj",
|
||||
_ => "o",
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a `StaticlibArtifact` from component parts.
|
||||
pub fn from_parts_crosscompiled(
|
||||
engine_inner: &mut StaticlibEngineInner,
|
||||
metadata: ModuleMetadata,
|
||||
module_bytes: Vec<u8>,
|
||||
metadata_length: usize,
|
||||
) -> Result<Self, CompileError> {
|
||||
let finished_functions: PrimaryMap<LocalFunctionIndex, FunctionBodyPtr> = PrimaryMap::new();
|
||||
let finished_function_call_trampolines: PrimaryMap<SignatureIndex, VMTrampoline> =
|
||||
PrimaryMap::new();
|
||||
let finished_dynamic_function_trampolines: PrimaryMap<FunctionIndex, FunctionBodyPtr> =
|
||||
PrimaryMap::new();
|
||||
let signature_registry = engine_inner.signatures();
|
||||
let signatures = metadata
|
||||
.compile_info
|
||||
.module
|
||||
.signatures
|
||||
.values()
|
||||
.map(|sig| signature_registry.register(sig))
|
||||
.collect::<PrimaryMap<_, _>>();
|
||||
|
||||
let symbol_registry = metadata.get_symbol_registry();
|
||||
Ok(Self {
|
||||
metadata,
|
||||
module_bytes,
|
||||
finished_functions: finished_functions.into_boxed_slice(),
|
||||
finished_function_call_trampolines: finished_function_call_trampolines
|
||||
.into_boxed_slice(),
|
||||
finished_dynamic_function_trampolines: finished_dynamic_function_trampolines
|
||||
.into_boxed_slice(),
|
||||
signatures: signatures.into_boxed_slice(),
|
||||
func_data_registry: engine_inner.func_data().clone(),
|
||||
metadata_length,
|
||||
symbol_registry,
|
||||
is_compiled: true,
|
||||
})
|
||||
}
|
||||
|
||||
/// Compile a data buffer into a `StaticlibArtifact`, which may then be instantiated.
|
||||
#[cfg(not(feature = "compiler"))]
|
||||
pub fn new(_engine: &StaticlibEngine, _data: &[u8]) -> Result<Self, CompileError> {
|
||||
Err(CompileError::Codegen(
|
||||
"Compilation is not enabled in the engine".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Deserialize a `StaticlibArtifact` from bytes.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The bytes must represent a serialized WebAssembly module.
|
||||
pub unsafe fn deserialize(
|
||||
engine: &StaticlibEngine,
|
||||
bytes: &[u8],
|
||||
) -> Result<Self, DeserializeError> {
|
||||
let metadata_len = MetadataHeader::parse(bytes)?;
|
||||
|
||||
let metadata: ModuleMetadata =
|
||||
bincode::deserialize(&bytes[MetadataHeader::LEN..][..metadata_len]).unwrap();
|
||||
|
||||
const WORD_SIZE: usize = mem::size_of::<usize>();
|
||||
let mut byte_buffer = [0u8; WORD_SIZE];
|
||||
|
||||
let mut cur_offset = MetadataHeader::LEN + metadata_len;
|
||||
byte_buffer[0..WORD_SIZE].clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]);
|
||||
cur_offset += WORD_SIZE;
|
||||
|
||||
let num_finished_functions = usize::from_ne_bytes(byte_buffer);
|
||||
let mut finished_functions = PrimaryMap::new();
|
||||
|
||||
let engine_inner = engine.inner();
|
||||
let signature_registry = engine_inner.signatures();
|
||||
let func_data_registry = engine_inner.func_data().clone();
|
||||
let mut sig_map: BTreeMap<SignatureIndex, VMSharedSignatureIndex> = BTreeMap::new();
|
||||
|
||||
let num_imported_functions = metadata.compile_info.module.num_imported_functions;
|
||||
// set up the imported functions first...
|
||||
for i in 0..num_imported_functions {
|
||||
let sig_idx = metadata.compile_info.module.functions[FunctionIndex::new(i)];
|
||||
let func_type = &metadata.compile_info.module.signatures[sig_idx];
|
||||
let vm_shared_idx = signature_registry.register(func_type);
|
||||
sig_map.insert(sig_idx, vm_shared_idx);
|
||||
}
|
||||
// read finished functions in order now...
|
||||
for i in 0..num_finished_functions {
|
||||
let local_func_idx = LocalFunctionIndex::new(i);
|
||||
let func_idx = metadata.compile_info.module.func_index(local_func_idx);
|
||||
let sig_idx = metadata.compile_info.module.functions[func_idx];
|
||||
let func_type = &metadata.compile_info.module.signatures[sig_idx];
|
||||
let vm_shared_idx = signature_registry.register(func_type);
|
||||
sig_map.insert(sig_idx, vm_shared_idx);
|
||||
|
||||
byte_buffer[0..WORD_SIZE]
|
||||
.clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]);
|
||||
let fp = FunctionBodyPtr(usize::from_ne_bytes(byte_buffer) as _);
|
||||
cur_offset += WORD_SIZE;
|
||||
|
||||
// TODO: we can read back the length here if we serialize it. This will improve debug output.
|
||||
|
||||
finished_functions.push(fp);
|
||||
}
|
||||
|
||||
let mut signatures: PrimaryMap<_, VMSharedSignatureIndex> = PrimaryMap::new();
|
||||
for i in 0..(sig_map.len()) {
|
||||
if let Some(shared_idx) = sig_map.get(&SignatureIndex::new(i)) {
|
||||
signatures.push(*shared_idx);
|
||||
} else {
|
||||
panic!("Invalid data, missing sig idx; TODO: handle this error");
|
||||
}
|
||||
}
|
||||
|
||||
// read trampolines in order
|
||||
let mut finished_function_call_trampolines = PrimaryMap::new();
|
||||
byte_buffer[0..WORD_SIZE].clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]);
|
||||
cur_offset += WORD_SIZE;
|
||||
let num_function_trampolines = usize::from_ne_bytes(byte_buffer);
|
||||
for _ in 0..num_function_trampolines {
|
||||
byte_buffer[0..WORD_SIZE]
|
||||
.clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]);
|
||||
cur_offset += WORD_SIZE;
|
||||
let trampoline_ptr_bytes = usize::from_ne_bytes(byte_buffer);
|
||||
let trampoline = mem::transmute::<usize, VMTrampoline>(trampoline_ptr_bytes);
|
||||
finished_function_call_trampolines.push(trampoline);
|
||||
// TODO: we can read back the length here if we serialize it. This will improve debug output.
|
||||
}
|
||||
|
||||
// read dynamic function trampolines in order now...
|
||||
let mut finished_dynamic_function_trampolines = PrimaryMap::new();
|
||||
byte_buffer[0..WORD_SIZE].clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]);
|
||||
cur_offset += WORD_SIZE;
|
||||
let num_dynamic_trampoline_functions = usize::from_ne_bytes(byte_buffer);
|
||||
for _i in 0..num_dynamic_trampoline_functions {
|
||||
byte_buffer[0..WORD_SIZE]
|
||||
.clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]);
|
||||
let fp = FunctionBodyPtr(usize::from_ne_bytes(byte_buffer) as _);
|
||||
cur_offset += WORD_SIZE;
|
||||
|
||||
// TODO: we can read back the length here if we serialize it. This will improve debug output.
|
||||
|
||||
finished_dynamic_function_trampolines.push(fp);
|
||||
}
|
||||
|
||||
let symbol_registry = metadata.get_symbol_registry();
|
||||
Ok(Self {
|
||||
metadata,
|
||||
module_bytes: bytes.to_owned(),
|
||||
finished_functions: finished_functions.into_boxed_slice(),
|
||||
finished_function_call_trampolines: finished_function_call_trampolines
|
||||
.into_boxed_slice(),
|
||||
finished_dynamic_function_trampolines: finished_dynamic_function_trampolines
|
||||
.into_boxed_slice(),
|
||||
signatures: signatures.into_boxed_slice(),
|
||||
func_data_registry,
|
||||
metadata_length: 0,
|
||||
symbol_registry,
|
||||
is_compiled: false,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the `SymbolRegistry` used to generate the names used in the Artifact.
|
||||
pub fn symbol_registry(&self) -> &dyn SymbolRegistry {
|
||||
&self.symbol_registry
|
||||
}
|
||||
|
||||
/// The length in bytes of the metadata in the serialized output.
|
||||
pub fn metadata_length(&self) -> usize {
|
||||
self.metadata_length
|
||||
}
|
||||
}
|
||||
|
||||
impl ArtifactCreate for StaticlibArtifact {
|
||||
fn module(&self) -> Arc<ModuleInfo> {
|
||||
self.metadata.compile_info.module.clone()
|
||||
}
|
||||
|
||||
fn module_ref(&self) -> &ModuleInfo {
|
||||
&self.metadata.compile_info.module
|
||||
}
|
||||
|
||||
fn module_mut(&mut self) -> Option<&mut ModuleInfo> {
|
||||
Arc::get_mut(&mut self.metadata.compile_info.module)
|
||||
}
|
||||
|
||||
fn features(&self) -> &Features {
|
||||
&self.metadata.compile_info.features
|
||||
}
|
||||
|
||||
fn cpu_features(&self) -> EnumSet<CpuFeature> {
|
||||
EnumSet::from_u64(self.metadata.cpu_features)
|
||||
}
|
||||
|
||||
fn data_initializers(&self) -> &[OwnedDataInitializer] {
|
||||
&*self.metadata.data_initializers
|
||||
}
|
||||
|
||||
fn memory_styles(&self) -> &PrimaryMap<MemoryIndex, MemoryStyle> {
|
||||
&self.metadata.compile_info.memory_styles
|
||||
}
|
||||
|
||||
fn table_styles(&self) -> &PrimaryMap<TableIndex, TableStyle> {
|
||||
&self.metadata.compile_info.table_styles
|
||||
}
|
||||
|
||||
/// Serialize a StaticlibArtifact
|
||||
fn serialize(&self) -> Result<Vec<u8>, SerializeError> {
|
||||
Ok(self.module_bytes.clone())
|
||||
}
|
||||
}
|
||||
impl Artifact for StaticlibArtifact {
|
||||
fn register_frame_info(&self) {
|
||||
// Do nothing for now
|
||||
}
|
||||
|
||||
fn finished_functions(&self) -> &BoxedSlice<LocalFunctionIndex, FunctionBodyPtr> {
|
||||
&self.finished_functions
|
||||
}
|
||||
|
||||
fn finished_function_call_trampolines(&self) -> &BoxedSlice<SignatureIndex, VMTrampoline> {
|
||||
&self.finished_function_call_trampolines
|
||||
}
|
||||
|
||||
fn finished_dynamic_function_trampolines(&self) -> &BoxedSlice<FunctionIndex, FunctionBodyPtr> {
|
||||
&self.finished_dynamic_function_trampolines
|
||||
}
|
||||
|
||||
fn signatures(&self) -> &BoxedSlice<SignatureIndex, VMSharedSignatureIndex> {
|
||||
&self.signatures
|
||||
}
|
||||
|
||||
fn func_data_registry(&self) -> &FuncDataRegistry {
|
||||
&self.func_data_registry
|
||||
}
|
||||
fn preinstantiate(&self) -> Result<(), InstantiationError> {
|
||||
if self.is_compiled {
|
||||
panic!(
|
||||
"a module built with the staticlib engine must be linked \
|
||||
into the current executable"
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
use crate::StaticlibEngine;
|
||||
use wasmer_compiler::{CompilerConfig, Features, Target};
|
||||
|
||||
/// The Staticlib builder
|
||||
pub struct Staticlib {
|
||||
compiler_config: Option<Box<dyn CompilerConfig>>,
|
||||
target: Option<Target>,
|
||||
features: Option<Features>,
|
||||
}
|
||||
|
||||
impl Staticlib {
|
||||
#[cfg(feature = "compiler")]
|
||||
/// Create a new Staticlib
|
||||
pub fn new<T>(compiler_config: T) -> Self
|
||||
where
|
||||
T: Into<Box<dyn CompilerConfig>>,
|
||||
{
|
||||
let mut compiler_config = compiler_config.into();
|
||||
compiler_config.enable_pic();
|
||||
|
||||
Self {
|
||||
compiler_config: Some(compiler_config),
|
||||
target: None,
|
||||
features: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new headless Staticlib
|
||||
pub fn headless() -> Self {
|
||||
Self {
|
||||
compiler_config: None,
|
||||
target: None,
|
||||
features: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the target
|
||||
pub fn target(mut self, target: Target) -> Self {
|
||||
self.target = Some(target);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the features
|
||||
pub fn features(mut self, features: Features) -> Self {
|
||||
self.features = Some(features);
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the `StaticlibEngine` for this configuration
|
||||
pub fn engine(self) -> StaticlibEngine {
|
||||
if let Some(_compiler_config) = self.compiler_config {
|
||||
#[cfg(feature = "compiler")]
|
||||
{
|
||||
let compiler_config = _compiler_config;
|
||||
let target = self.target.unwrap_or_default();
|
||||
let features = self
|
||||
.features
|
||||
.unwrap_or_else(|| compiler_config.default_features_for_target(&target));
|
||||
let compiler = compiler_config.compiler();
|
||||
StaticlibEngine::new(compiler, target, features)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "compiler"))]
|
||||
{
|
||||
unreachable!("Cannot call `StaticlibEngine::new` without the `compiler` feature")
|
||||
}
|
||||
} else {
|
||||
StaticlibEngine::headless()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[cfg(feature = "compiler")]
|
||||
use std::sync::Arc;
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_compiler::{Compiler, ModuleMiddleware};
|
||||
|
||||
#[cfg(feature = "compiler")]
|
||||
#[derive(Default)]
|
||||
pub struct TestCompilerConfig {
|
||||
pub enabled_pic: bool,
|
||||
pub middlewares: Vec<Arc<dyn ModuleMiddleware>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "compiler")]
|
||||
impl CompilerConfig for TestCompilerConfig {
|
||||
fn enable_pic(&mut self) {
|
||||
self.enabled_pic = true;
|
||||
}
|
||||
|
||||
fn compiler(self: Box<Self>) -> Box<dyn Compiler> {
|
||||
unimplemented!("compiler not implemented");
|
||||
}
|
||||
|
||||
fn push_middleware(&mut self, middleware: Arc<dyn ModuleMiddleware>) {
|
||||
self.middlewares.push(middleware);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "compiler")]
|
||||
#[test]
|
||||
#[should_panic(expected = "compiler not implemented")]
|
||||
fn build_engine() {
|
||||
let compiler_config = TestCompilerConfig::default();
|
||||
let staticlib = Staticlib::new(compiler_config);
|
||||
let _engine = staticlib.engine();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_headless_engine() {
|
||||
let staticlib = Staticlib::headless();
|
||||
let _engine = staticlib.engine();
|
||||
}
|
||||
}
|
||||
@@ -1,254 +0,0 @@
|
||||
use crate::StaticlibArtifact;
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_compiler::Compiler;
|
||||
use wasmer_compiler::{CompileError, Target};
|
||||
use wasmer_engine::{Artifact, DeserializeError, Engine, EngineId, Tunables};
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_types::Features;
|
||||
use wasmer_types::FunctionType;
|
||||
use wasmer_vm::{
|
||||
FuncDataRegistry, SignatureRegistry, VMCallerCheckedAnyfunc, VMFuncRef, VMSharedSignatureIndex,
|
||||
};
|
||||
|
||||
/// A WebAssembly `Staticlib` Engine.
|
||||
#[derive(Clone)]
|
||||
pub struct StaticlibEngine {
|
||||
inner: Arc<Mutex<StaticlibEngineInner>>,
|
||||
/// The target for the compiler
|
||||
target: Arc<Target>,
|
||||
engine_id: EngineId,
|
||||
}
|
||||
|
||||
impl StaticlibEngine {
|
||||
/// Create a new `StaticlibEngine` with the given config
|
||||
#[cfg(feature = "compiler")]
|
||||
pub fn new(compiler: Box<dyn Compiler>, target: Target, features: Features) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(Mutex::new(StaticlibEngineInner {
|
||||
compiler: Some(compiler),
|
||||
signatures: SignatureRegistry::new(),
|
||||
func_data: Arc::new(FuncDataRegistry::new()),
|
||||
prefixer: None,
|
||||
features,
|
||||
})),
|
||||
target: Arc::new(target),
|
||||
engine_id: EngineId::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a headless `StaticlibEngine`
|
||||
///
|
||||
/// A headless engine is an engine without any compiler attached.
|
||||
/// This is useful for assuring a minimal runtime for running
|
||||
/// WebAssembly modules.
|
||||
///
|
||||
/// For example, for running in IoT devices where compilers are very
|
||||
/// expensive, or also to optimize startup speed.
|
||||
///
|
||||
/// # Important
|
||||
///
|
||||
/// Headless engines can't compile or validate any modules,
|
||||
/// they just take already processed Modules (via `Module::serialize`).
|
||||
pub fn headless() -> Self {
|
||||
Self {
|
||||
inner: Arc::new(Mutex::new(StaticlibEngineInner {
|
||||
#[cfg(feature = "compiler")]
|
||||
compiler: None,
|
||||
#[cfg(feature = "compiler")]
|
||||
features: Features::default(),
|
||||
signatures: SignatureRegistry::new(),
|
||||
func_data: Arc::new(FuncDataRegistry::new()),
|
||||
prefixer: None,
|
||||
})),
|
||||
target: Arc::new(Target::default()),
|
||||
engine_id: EngineId::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets a prefixer for the Wasm module, so we can avoid any
|
||||
/// collisions in the exported function names on the generated
|
||||
/// object.
|
||||
///
|
||||
/// This, allows us to rather than have functions named
|
||||
/// `wasmer_function_1` to be named `wasmer_function_PREFIX_1`.
|
||||
///
|
||||
/// # Important
|
||||
///
|
||||
/// This prefixer function should be deterministic, so the
|
||||
/// compilation remains deterministic.
|
||||
pub fn set_deterministic_prefixer<F>(&mut self, prefixer: F)
|
||||
where
|
||||
F: Fn(&[u8]) -> String + Send + 'static,
|
||||
{
|
||||
let mut inner = self.inner_mut();
|
||||
inner.prefixer = Some(Box::new(prefixer));
|
||||
}
|
||||
|
||||
pub(crate) fn inner(&self) -> std::sync::MutexGuard<'_, StaticlibEngineInner> {
|
||||
self.inner.lock().unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn inner_mut(&self) -> std::sync::MutexGuard<'_, StaticlibEngineInner> {
|
||||
self.inner.lock().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Engine for StaticlibEngine {
|
||||
/// The target
|
||||
fn target(&self) -> &Target {
|
||||
&self.target
|
||||
}
|
||||
|
||||
/// Register a signature
|
||||
fn register_signature(&self, func_type: &FunctionType) -> VMSharedSignatureIndex {
|
||||
let compiler = self.inner();
|
||||
compiler.signatures().register(func_type)
|
||||
}
|
||||
|
||||
fn register_function_metadata(&self, func_data: VMCallerCheckedAnyfunc) -> VMFuncRef {
|
||||
let compiler = self.inner();
|
||||
compiler.func_data().register(func_data)
|
||||
}
|
||||
|
||||
/// Lookup a signature
|
||||
fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option<FunctionType> {
|
||||
let compiler = self.inner();
|
||||
compiler.signatures().lookup(sig)
|
||||
}
|
||||
|
||||
/// Validates a WebAssembly module
|
||||
fn validate(&self, binary: &[u8]) -> Result<(), CompileError> {
|
||||
self.inner().validate(binary)
|
||||
}
|
||||
|
||||
/// Compile a WebAssembly binary
|
||||
#[cfg(feature = "compiler")]
|
||||
fn compile(
|
||||
&self,
|
||||
binary: &[u8],
|
||||
tunables: &dyn Tunables,
|
||||
) -> Result<Arc<dyn Artifact>, CompileError> {
|
||||
Ok(Arc::new(StaticlibArtifact::new(self, binary, tunables)?))
|
||||
}
|
||||
|
||||
/// Compile a WebAssembly binary (it will fail because the `compiler` flag is disabled).
|
||||
#[cfg(not(feature = "compiler"))]
|
||||
fn compile(
|
||||
&self,
|
||||
_binary: &[u8],
|
||||
_tunables: &dyn Tunables,
|
||||
) -> Result<Arc<dyn Artifact>, CompileError> {
|
||||
Err(CompileError::Codegen(
|
||||
"The `StaticlibEngine` is operating in headless mode, so it cannot compile a module."
|
||||
.to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Deserializes a WebAssembly module (binary content of a static object file)
|
||||
unsafe fn deserialize(&self, bytes: &[u8]) -> Result<Arc<dyn Artifact>, DeserializeError> {
|
||||
Ok(Arc::new(StaticlibArtifact::deserialize(self, bytes)?))
|
||||
}
|
||||
|
||||
/// Deserializes a WebAssembly module from a path
|
||||
///
|
||||
/// It should point to a static object file generated by this
|
||||
/// engine.
|
||||
unsafe fn deserialize_from_file(
|
||||
&self,
|
||||
file_ref: &Path,
|
||||
) -> Result<Arc<dyn Artifact>, DeserializeError> {
|
||||
let mut f = std::fs::File::open(file_ref)?;
|
||||
let mut vec = vec![];
|
||||
f.read_to_end(&mut vec)?;
|
||||
|
||||
self.deserialize(&vec[..])
|
||||
}
|
||||
|
||||
fn id(&self) -> &EngineId {
|
||||
&self.engine_id
|
||||
}
|
||||
|
||||
fn cloned(&self) -> Arc<dyn Engine + Send + Sync> {
|
||||
Arc::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// The inner contents of `StaticlibEngine`
|
||||
pub struct StaticlibEngineInner {
|
||||
/// The compiler
|
||||
#[cfg(feature = "compiler")]
|
||||
compiler: Option<Box<dyn Compiler>>,
|
||||
|
||||
/// The WebAssembly features to use
|
||||
#[cfg(feature = "compiler")]
|
||||
features: Features,
|
||||
|
||||
/// The signature registry is used mainly to operate with trampolines
|
||||
/// performantly.
|
||||
signatures: SignatureRegistry,
|
||||
|
||||
/// The backing storage of `VMFuncRef`s. This centralized store ensures that 2
|
||||
/// functions with the same `VMCallerCheckedAnyfunc` will have the same `VMFuncRef`.
|
||||
/// It also guarantees that the `VMFuncRef`s stay valid until the engine is dropped.
|
||||
func_data: Arc<FuncDataRegistry>,
|
||||
|
||||
/// The prefixer returns the a String to prefix each of the
|
||||
/// functions in the static object generated by the
|
||||
/// `StaticlibEngine`, so we can assure no collisions.
|
||||
prefixer: Option<Box<dyn Fn(&[u8]) -> String + Send>>,
|
||||
}
|
||||
|
||||
impl StaticlibEngineInner {
|
||||
/// Gets the compiler associated to this engine.
|
||||
#[cfg(feature = "compiler")]
|
||||
pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> {
|
||||
if self.compiler.is_none() {
|
||||
return Err(CompileError::Codegen("The `StaticlibEngine` is operating in headless mode, so it can only execute already compiled Modules.".to_string()));
|
||||
}
|
||||
Ok(&**self
|
||||
.compiler
|
||||
.as_ref()
|
||||
.expect("Can't get compiler reference"))
|
||||
}
|
||||
|
||||
#[cfg(feature = "compiler")]
|
||||
pub(crate) fn get_prefix(&self, bytes: &[u8]) -> String {
|
||||
if let Some(prefixer) = &self.prefixer {
|
||||
prefixer(bytes)
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "compiler")]
|
||||
pub(crate) fn features(&self) -> &Features {
|
||||
&self.features
|
||||
}
|
||||
|
||||
/// Validate the module
|
||||
#[cfg(feature = "compiler")]
|
||||
pub fn validate(&self, data: &[u8]) -> Result<(), CompileError> {
|
||||
self.compiler()?.validate_module(self.features(), data)
|
||||
}
|
||||
|
||||
/// Validate the module
|
||||
#[cfg(not(feature = "compiler"))]
|
||||
pub fn validate(&self, _data: &[u8]) -> Result<(), CompileError> {
|
||||
Err(CompileError::Validate(
|
||||
"The `StaticlibEngine` is not compiled with compiler support, which is required for validating".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Shared signature registry.
|
||||
pub fn signatures(&self) -> &SignatureRegistry {
|
||||
&self.signatures
|
||||
}
|
||||
|
||||
/// Shared func metadata registry.
|
||||
pub(crate) fn func_data(&self) -> &Arc<FuncDataRegistry> {
|
||||
&self.func_data
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
//! Staticlib engine for Wasmer compilers.
|
||||
//!
|
||||
//! Given a compiler (such as `CraneliftCompiler` or `LLVMCompiler`)
|
||||
//! it generates a static object file (`.o` file) and metadata which
|
||||
//! can be used to access it from other programming languages static.
|
||||
|
||||
#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
|
||||
#![warn(unused_import_braces)]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
|
||||
#![cfg_attr(
|
||||
feature = "cargo-clippy",
|
||||
warn(
|
||||
clippy::float_arithmetic,
|
||||
clippy::mut_mut,
|
||||
clippy::nonminimal_bool,
|
||||
clippy::map_unwrap_or,
|
||||
clippy::print_stdout,
|
||||
clippy::unicode_not_nfc,
|
||||
clippy::use_self
|
||||
)
|
||||
)]
|
||||
|
||||
mod artifact;
|
||||
mod builder;
|
||||
mod engine;
|
||||
mod serialize;
|
||||
|
||||
pub use crate::artifact::StaticlibArtifact;
|
||||
pub use crate::builder::Staticlib;
|
||||
pub use crate::engine::StaticlibEngine;
|
||||
|
||||
/// Version number of this crate.
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
@@ -1,91 +0,0 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use wasmer_compiler::{CompileModuleInfo, SectionIndex, Symbol, SymbolRegistry};
|
||||
use wasmer_types::entity::{EntityRef, PrimaryMap};
|
||||
use wasmer_types::{FunctionIndex, LocalFunctionIndex, OwnedDataInitializer, SignatureIndex};
|
||||
|
||||
/// Serializable struct that represents the compiled metadata.
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct ModuleMetadata {
|
||||
pub compile_info: CompileModuleInfo,
|
||||
pub prefix: String,
|
||||
pub data_initializers: Box<[OwnedDataInitializer]>,
|
||||
// The function body lengths (used to find function by address)
|
||||
pub function_body_lengths: PrimaryMap<LocalFunctionIndex, u64>,
|
||||
pub cpu_features: u64,
|
||||
}
|
||||
|
||||
pub struct ModuleMetadataSymbolRegistry {
|
||||
pub prefix: String,
|
||||
}
|
||||
|
||||
impl ModuleMetadata {
|
||||
pub fn split(&mut self) -> (&mut CompileModuleInfo, ModuleMetadataSymbolRegistry) {
|
||||
let compile_info = &mut self.compile_info;
|
||||
let symbol_registry = ModuleMetadataSymbolRegistry {
|
||||
prefix: self.prefix.clone(),
|
||||
};
|
||||
(compile_info, symbol_registry)
|
||||
}
|
||||
|
||||
pub fn get_symbol_registry(&self) -> ModuleMetadataSymbolRegistry {
|
||||
ModuleMetadataSymbolRegistry {
|
||||
prefix: self.prefix.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SymbolRegistry for ModuleMetadataSymbolRegistry {
|
||||
fn symbol_to_name(&self, symbol: Symbol) -> String {
|
||||
match symbol {
|
||||
Symbol::LocalFunction(index) => {
|
||||
format!("wasmer_function_{}_{}", self.prefix, index.index())
|
||||
}
|
||||
Symbol::Section(index) => format!("wasmer_section_{}_{}", self.prefix, index.index()),
|
||||
Symbol::FunctionCallTrampoline(index) => {
|
||||
format!(
|
||||
"wasmer_trampoline_function_call_{}_{}",
|
||||
self.prefix,
|
||||
index.index()
|
||||
)
|
||||
}
|
||||
Symbol::DynamicFunctionTrampoline(index) => {
|
||||
format!(
|
||||
"wasmer_trampoline_dynamic_function_{}_{}",
|
||||
self.prefix,
|
||||
index.index()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn name_to_symbol(&self, name: &str) -> Option<Symbol> {
|
||||
if let Some(index) = name.strip_prefix(&format!("wasmer_function_{}_", self.prefix)) {
|
||||
index
|
||||
.parse::<u32>()
|
||||
.ok()
|
||||
.map(|index| Symbol::LocalFunction(LocalFunctionIndex::from_u32(index)))
|
||||
} else if let Some(index) = name.strip_prefix(&format!("wasmer_section_{}_", self.prefix)) {
|
||||
index
|
||||
.parse::<u32>()
|
||||
.ok()
|
||||
.map(|index| Symbol::Section(SectionIndex::from_u32(index)))
|
||||
} else if let Some(index) =
|
||||
name.strip_prefix(&format!("wasmer_trampoline_function_call_{}_", self.prefix))
|
||||
{
|
||||
index
|
||||
.parse::<u32>()
|
||||
.ok()
|
||||
.map(|index| Symbol::FunctionCallTrampoline(SignatureIndex::from_u32(index)))
|
||||
} else if let Some(index) = name.strip_prefix(&format!(
|
||||
"wasmer_trampoline_dynamic_function_{}_",
|
||||
self.prefix
|
||||
)) {
|
||||
index
|
||||
.parse::<u32>()
|
||||
.ok()
|
||||
.map(|index| Symbol::DynamicFunctionTrampoline(FunctionIndex::from_u32(index)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,9 @@ Wasmer Engines are mainly responsible for two things:
|
||||
* **Load** an `Artifact` so it can be used by the user (normally,
|
||||
pushing the code into executable memory and so on).
|
||||
|
||||
It currently has two implementations:
|
||||
It currently has one implementation:
|
||||
|
||||
1. Universal with [`wasmer-engine-universal`],
|
||||
2. Object with [`wasmer-engine-staticlib`].
|
||||
|
||||
## Example Implementation
|
||||
|
||||
@@ -28,7 +27,6 @@ attributions of the project.
|
||||
|
||||
|
||||
[`wasmer-engine-universal`]: https://github.com/wasmerio/wasmer/tree/master/lib/engine-universal
|
||||
[`wasmer-engine-staticlib`]: https://github.com/wasmerio/wasmer/tree/master/lib/engine-staticlib
|
||||
[`wasmer-engine-dummy`]: https://github.com/wasmerio/wasmer/tree/master/tests/lib/engine-dummy
|
||||
[`wasmtime-api`]: https://crates.io/crates/wasmtime
|
||||
[Wasmer `ATTRIBUTIONS`]: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md
|
||||
|
||||
@@ -13,7 +13,6 @@ attributions of the project.
|
||||
|
||||
|
||||
[`wasmer-engine-universal`]: https://github.com/wasmerio/wasmer/tree/master/lib/engine-universal
|
||||
[`wasmer-engine-staticlib`]: https://github.com/wasmerio/wasmer/tree/master/lib/engine-staticlib
|
||||
[`wasmer-engine-dummy`]: https://github.com/wasmerio/wasmer/tree/master/tests/lib/engine-dummy
|
||||
[`wasmtime-api`]: https://crates.io/crates/wasmtime
|
||||
[Wasmer `ATTRIBUTIONS`]: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md
|
||||
|
||||
@@ -59,16 +59,6 @@ dep_graph = {
|
||||
"wasmer-engine",
|
||||
]
|
||||
),
|
||||
"wasmer-engine-staticlib": set(
|
||||
[
|
||||
"wasmer-artifact",
|
||||
"wasmer-types",
|
||||
"wasmer-compiler",
|
||||
"wasmer-vm",
|
||||
"wasmer-engine",
|
||||
"wasmer-object",
|
||||
]
|
||||
),
|
||||
"wasmer": set(
|
||||
[
|
||||
"wasmer-artifact",
|
||||
@@ -99,7 +89,6 @@ dep_graph = {
|
||||
"wasmer-emscripten",
|
||||
"wasmer-engine",
|
||||
"wasmer-engine-universal",
|
||||
"wasmer-engine-staticlib",
|
||||
"wasmer-middlewares",
|
||||
"wasmer-wasi",
|
||||
"wasmer-types",
|
||||
@@ -117,7 +106,6 @@ dep_graph = {
|
||||
"wasmer-emscripten",
|
||||
"wasmer-engine",
|
||||
"wasmer-engine-universal",
|
||||
"wasmer-engine-staticlib",
|
||||
"wasmer-vm",
|
||||
"wasmer-wasi",
|
||||
"wasmer-wasi-experimental-io-devices",
|
||||
@@ -146,7 +134,6 @@ location = {
|
||||
"wasmer-compiler-llvm": "compiler-llvm",
|
||||
"wasmer-engine": "engine",
|
||||
"wasmer-engine-universal": "engine-universal",
|
||||
"wasmer-engine-staticlib": "engine-staticlib",
|
||||
"wasmer-cache": "cache",
|
||||
"wasmer": "api",
|
||||
"wasmer-wasi": "wasi",
|
||||
|
||||
@@ -22,14 +22,12 @@ impl Compiler {
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Engine {
|
||||
Universal,
|
||||
Staticlib,
|
||||
}
|
||||
|
||||
impl Engine {
|
||||
pub const fn to_flag(self) -> &'static str {
|
||||
match self {
|
||||
Engine::Universal => "--universal",
|
||||
Engine::Staticlib => "--staticlib",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
//! CLI tests for the compile subcommand.
|
||||
|
||||
use anyhow::{bail, Context};
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use wasmer_integration_tests_cli::link_code::*;
|
||||
use wasmer_integration_tests_cli::*;
|
||||
|
||||
const STATICLIB_ENGINE_TEST_C_SOURCE: &[u8] = include_bytes!("staticlib_engine_test_c_source.c");
|
||||
|
||||
fn staticlib_engine_test_wasm_path() -> String {
|
||||
format!("{}/{}", C_ASSET_PATH, "qjs.wasm")
|
||||
}
|
||||
|
||||
/// Data used to run the `wasmer compile` command.
|
||||
#[derive(Debug)]
|
||||
struct WasmerCompile {
|
||||
/// The directory to operate in.
|
||||
current_dir: PathBuf,
|
||||
/// Path to wasmer executable used to run the command.
|
||||
wasmer_path: PathBuf,
|
||||
/// Path to the Wasm file to compile.
|
||||
wasm_path: PathBuf,
|
||||
/// Path to the static object file produced by compiling the Wasm.
|
||||
wasm_object_path: PathBuf,
|
||||
/// Path to output the generated header to.
|
||||
header_output_path: PathBuf,
|
||||
/// Compiler with which to compile the Wasm.
|
||||
compiler: Compiler,
|
||||
/// Engine with which to use to generate the artifacts.
|
||||
engine: Engine,
|
||||
}
|
||||
|
||||
impl Default for WasmerCompile {
|
||||
fn default() -> Self {
|
||||
#[cfg(not(windows))]
|
||||
let wasm_obj_path = "wasm.o";
|
||||
#[cfg(windows)]
|
||||
let wasm_obj_path = "wasm.obj";
|
||||
Self {
|
||||
current_dir: std::env::current_dir().unwrap(),
|
||||
wasmer_path: get_wasmer_path(),
|
||||
wasm_path: PathBuf::from(staticlib_engine_test_wasm_path()),
|
||||
wasm_object_path: PathBuf::from(wasm_obj_path),
|
||||
header_output_path: PathBuf::from("my_wasm.h"),
|
||||
compiler: Compiler::Cranelift,
|
||||
engine: Engine::Staticlib,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WasmerCompile {
|
||||
fn run(&self) -> anyhow::Result<()> {
|
||||
let output = Command::new(&self.wasmer_path)
|
||||
.current_dir(&self.current_dir)
|
||||
.arg("compile")
|
||||
.arg(&self.wasm_path.canonicalize()?)
|
||||
.arg(&self.compiler.to_flag())
|
||||
.arg(&self.engine.to_flag())
|
||||
.arg("-o")
|
||||
.arg(&self.wasm_object_path)
|
||||
.arg("--header")
|
||||
.arg(&self.header_output_path)
|
||||
.output()?;
|
||||
|
||||
if !output.status.success() {
|
||||
bail!(
|
||||
"wasmer compile failed with: stdout: {}\n\nstderr: {}",
|
||||
std::str::from_utf8(&output.stdout)
|
||||
.expect("stdout is not utf8! need to handle arbitrary bytes"),
|
||||
std::str::from_utf8(&output.stderr)
|
||||
.expect("stderr is not utf8! need to handle arbitrary bytes")
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Compile the C code.
|
||||
fn run_c_compile(
|
||||
current_dir: &Path,
|
||||
path_to_c_src: &Path,
|
||||
output_name: &Path,
|
||||
) -> anyhow::Result<()> {
|
||||
#[cfg(not(windows))]
|
||||
let c_compiler = "cc";
|
||||
#[cfg(windows)]
|
||||
let c_compiler = "clang++";
|
||||
|
||||
let output = Command::new(c_compiler)
|
||||
.current_dir(current_dir)
|
||||
.arg("-O2")
|
||||
.arg("-c")
|
||||
.arg(path_to_c_src)
|
||||
.arg("-I")
|
||||
.arg(WASMER_INCLUDE_PATH)
|
||||
.arg("-o")
|
||||
.arg(output_name)
|
||||
.output()?;
|
||||
|
||||
if !output.status.success() {
|
||||
bail!(
|
||||
"C code compile failed with: stdout: {}\n\nstderr: {}",
|
||||
std::str::from_utf8(&output.stdout)
|
||||
.expect("stdout is not utf8! need to handle arbitrary bytes"),
|
||||
std::str::from_utf8(&output.stderr)
|
||||
.expect("stderr is not utf8! need to handle arbitrary bytes")
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn staticlib_engine_works() -> anyhow::Result<()> {
|
||||
let temp_dir = tempfile::tempdir().context("Making a temp dir")?;
|
||||
let operating_dir: PathBuf = temp_dir.path().to_owned();
|
||||
|
||||
let wasm_path = operating_dir.join(staticlib_engine_test_wasm_path());
|
||||
#[cfg(not(windows))]
|
||||
let wasm_object_path = operating_dir.join("wasm.o");
|
||||
#[cfg(windows)]
|
||||
let wasm_object_path = operating_dir.join("wasm.obj");
|
||||
let header_output_path = operating_dir.join("my_wasm.h");
|
||||
|
||||
WasmerCompile {
|
||||
current_dir: operating_dir.clone(),
|
||||
wasm_path,
|
||||
wasm_object_path: wasm_object_path.clone(),
|
||||
header_output_path,
|
||||
compiler: Compiler::Cranelift,
|
||||
engine: Engine::Staticlib,
|
||||
..Default::default()
|
||||
}
|
||||
.run()
|
||||
.context("Failed to compile wasm with Wasmer")?;
|
||||
|
||||
let c_src_file_name = operating_dir.join("c_src.c");
|
||||
#[cfg(not(windows))]
|
||||
let c_object_path = operating_dir.join("c_src.o");
|
||||
#[cfg(windows)]
|
||||
let c_object_path = operating_dir.join("c_src.obj");
|
||||
let executable_path = operating_dir.join("a.out");
|
||||
|
||||
// TODO: adjust C source code based on locations of things
|
||||
{
|
||||
let mut c_src_file = fs::OpenOptions::new()
|
||||
.create_new(true)
|
||||
.write(true)
|
||||
.open(&c_src_file_name)
|
||||
.context("Failed to open C source code file")?;
|
||||
c_src_file.write_all(STATICLIB_ENGINE_TEST_C_SOURCE)?;
|
||||
}
|
||||
run_c_compile(&operating_dir, &c_src_file_name, &c_object_path)
|
||||
.context("Failed to compile C source code")?;
|
||||
LinkCode {
|
||||
current_dir: operating_dir.clone(),
|
||||
object_paths: vec![c_object_path, wasm_object_path],
|
||||
output_path: executable_path.clone(),
|
||||
..Default::default()
|
||||
}
|
||||
.run()
|
||||
.context("Failed to link objects together")?;
|
||||
|
||||
let result = run_code(&operating_dir, &executable_path, &[])
|
||||
.context("Failed to run generated executable")?;
|
||||
let result_lines = result.lines().collect::<Vec<&str>>();
|
||||
assert_eq!(result_lines, vec!["Initializing...", "\"Hello, World\""],);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
#include "wasmer.h"
|
||||
#include "my_wasm.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define own
|
||||
|
||||
static void print_wasmer_error() {
|
||||
int error_len = wasmer_last_error_length();
|
||||
if (error_len > 0) {
|
||||
printf("Error len: `%d`\n", error_len);
|
||||
char *error_str = (char *)malloc(error_len);
|
||||
wasmer_last_error_message(error_str, error_len);
|
||||
printf("Error str: `%s`\n", error_str);
|
||||
free(error_str);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("Initializing...\n");
|
||||
wasm_config_t *config = wasm_config_new();
|
||||
wasm_config_set_engine(config, STATICLIB);
|
||||
wasm_engine_t *engine = wasm_engine_new_with_config(config);
|
||||
wasm_store_t *store = wasm_store_new(engine);
|
||||
|
||||
wasm_module_t *module = wasmer_staticlib_engine_new(store, "qjs.wasm");
|
||||
|
||||
if (!module) {
|
||||
printf("Failed to create module\n");
|
||||
print_wasmer_error();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// We have now finished the memory buffer book keeping and we have a valid
|
||||
// Module.
|
||||
|
||||
// In this example we're passing some JavaScript source code as a command line
|
||||
// argument to a WASI module that can evaluate JavaScript.
|
||||
wasi_config_t *wasi_config = wasi_config_new("constant_value_here");
|
||||
const char *js_string =
|
||||
"function greet(name) { return JSON.stringify('Hello, ' + name); }; "
|
||||
"print(greet('World'));";
|
||||
wasi_config_arg(wasi_config, "--eval");
|
||||
wasi_config_arg(wasi_config, js_string);
|
||||
wasi_env_t *wasi_env = wasi_env_new(wasi_config);
|
||||
|
||||
if (!wasi_env) {
|
||||
printf("> Error building WASI env!\n");
|
||||
print_wasmer_error();
|
||||
return 1;
|
||||
}
|
||||
|
||||
wasm_importtype_vec_t import_types;
|
||||
wasm_module_imports(module, &import_types);
|
||||
|
||||
wasm_extern_vec_t imports;
|
||||
wasm_extern_vec_new_uninitialized(&imports, import_types.size);
|
||||
wasm_importtype_vec_delete(&import_types);
|
||||
|
||||
bool get_imports_result = wasi_get_imports(store, module, wasi_env, &imports);
|
||||
wasi_env_delete(wasi_env);
|
||||
|
||||
if (!get_imports_result) {
|
||||
printf("> Error getting WASI imports!\n");
|
||||
print_wasmer_error();
|
||||
return 1;
|
||||
}
|
||||
|
||||
wasm_instance_t *instance = wasm_instance_new(store, module, &imports, NULL);
|
||||
|
||||
if (!instance) {
|
||||
printf("Failed to create instance\n");
|
||||
print_wasmer_error();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// WASI is now set up.
|
||||
own wasm_func_t *start_function = wasi_get_start_function(instance);
|
||||
if (!start_function) {
|
||||
fprintf(stderr, "`_start` function not found\n");
|
||||
print_wasmer_error();
|
||||
return -1;
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
|
||||
wasm_val_vec_t args = WASM_EMPTY_VEC;
|
||||
wasm_val_vec_t results = WASM_EMPTY_VEC;
|
||||
own wasm_trap_t *trap = wasm_func_call(start_function, &args, &results);
|
||||
if (trap) {
|
||||
fprintf(stderr, "Trap is not NULL: TODO:\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
wasm_instance_delete(instance);
|
||||
wasm_module_delete(module);
|
||||
wasm_store_delete(store);
|
||||
wasm_engine_delete(engine);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user