Remove engine-staticlib

This commit is contained in:
Manos Pitsidianakis
2022-05-10 12:48:15 +03:00
parent e9d54e130c
commit 8be0089893
33 changed files with 16 additions and 3057 deletions

23
Cargo.lock generated
View File

@@ -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",

View File

@@ -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",

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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/

View File

@@ -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

View File

@@ -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",

View File

@@ -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 }))
}

View File

@@ -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");
}
}

View File

@@ -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",

View File

@@ -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"
);
}
}

View File

@@ -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)
}

View File

@@ -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")]

View File

@@ -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::*};

View File

@@ -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(())
}
}

View File

@@ -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(())
}
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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()

View File

@@ -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" }

View File

@@ -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!

View File

@@ -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(())
}
}

View File

@@ -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();
}
}

View File

@@ -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
}
}

View File

@@ -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");

View File

@@ -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
}
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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",

View File

@@ -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",
}
}
}

View File

@@ -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(())
}

View File

@@ -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;
}