From 5dbab5655ec0302396abe6453e9fbb998121ace4 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 8 Oct 2020 15:05:13 +0200 Subject: [PATCH 01/49] feat(c-api) Use `Option>` in `wasm_valtype_kind`. A more Rust-FFI idiomatic way to handle null pointer. Note: In `wasm_valtype_kind`, it's tricky to handle the error because we _must_ return a `wasm_valtype_kind` value. For the moment, it continues to panic, which is probably the best tradeoff. --- lib/c-api/src/wasm_c_api/types/value.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/c-api/src/wasm_c_api/types/value.rs b/lib/c-api/src/wasm_c_api/types/value.rs index 542833638..023539c36 100644 --- a/lib/c-api/src/wasm_c_api/types/value.rs +++ b/lib/c-api/src/wasm_c_api/types/value.rs @@ -1,5 +1,6 @@ use super::super::value::wasm_valkind_t; use std::convert::TryInto; +use std::ptr::NonNull; use wasmer::ValType; #[allow(non_camel_case_types)] @@ -83,10 +84,11 @@ pub extern "C" fn wasm_valtype_new(kind: wasm_valkind_t) -> Option>) {} #[no_mangle] -pub unsafe extern "C" fn wasm_valtype_kind(valtype: *const wasm_valtype_t) -> wasm_valkind_t { - if valtype.is_null() { - // TODO: handle error - panic!("wasm_valtype_kind: argument is null pointer"); - } - return (*valtype).valkind as wasm_valkind_t; +pub unsafe extern "C" fn wasm_valtype_kind( + valtype: Option>, +) -> wasm_valkind_t { + valtype + .expect("`wasm_valtype_kind: argument is a null pointer") + .as_ref() + .valkind as wasm_valkind_t } From 65265dbd73c01c8660ed79b570ebef9de8e07a2c Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 8 Oct 2020 14:13:54 -0700 Subject: [PATCH 02/49] Add first draft of `wasmer create-exe` --- Cargo.lock | 1 + lib/c-api/wasmer_wasm.h | 3 + lib/cli/Cargo.toml | 1 + lib/cli/src/bin/wasmer.rs | 9 + lib/cli/src/commands.rs | 4 + lib/cli/src/commands/compile.rs | 5 +- lib/cli/src/commands/create_exe.rs | 273 +++++++++++++++++ lib/cli/src/commands/wasmer_create_exe_main.c | 89 ++++++ lib/cli/src/store.rs | 286 ++++++++++-------- 9 files changed, 543 insertions(+), 128 deletions(-) create mode 100644 lib/cli/src/commands/create_exe.rs create mode 100644 lib/cli/src/commands/wasmer_create_exe_main.c diff --git a/Cargo.lock b/Cargo.lock index e27fa5c4a..3ce702103 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2213,6 +2213,7 @@ dependencies = [ "fern", "log", "structopt", + "tempfile", "wasmer", "wasmer-cache", "wasmer-compiler", diff --git a/lib/c-api/wasmer_wasm.h b/lib/c-api/wasmer_wasm.h index 885102827..b9eea99ae 100644 --- a/lib/c-api/wasmer_wasm.h +++ b/lib/c-api/wasmer_wasm.h @@ -28,6 +28,9 @@ # define DEPRECATED(message) __declspec(deprecated(message)) #endif +// The `jit` feature has been enabled for this build. +#define WASMER_JIT_ENABLED + // The `compiler` feature has been enabled for this build. #define WASMER_COMPILER_ENABLED diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index b73828bf8..19f353b9c 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -45,6 +45,7 @@ cfg-if = "0.1" # For debug feature fern = { version = "0.6", features = ["colored"], optional = true } log = { version = "0.4", optional = true } +tempfile = "3" [features] # Don't add the compiler features in default, please add them on the Makefile diff --git a/lib/cli/src/bin/wasmer.rs b/lib/cli/src/bin/wasmer.rs index 2b1d5792e..6d72964bf 100644 --- a/lib/cli/src/bin/wasmer.rs +++ b/lib/cli/src/bin/wasmer.rs @@ -1,4 +1,6 @@ use anyhow::Result; +#[cfg(feature = "object-file")] +use wasmer_cli::commands::CreateExe; #[cfg(feature = "wast")] use wasmer_cli::commands::Wast; use wasmer_cli::commands::{Cache, Compile, Config, Inspect, Run, SelfUpdate, Validate}; @@ -26,6 +28,11 @@ enum WasmerCLIOptions { #[structopt(name = "compile")] Compile(Compile), + /// Compile a WebAssembly binary into a native executable + #[cfg(feature = "object-file")] + #[structopt(name = "create-exe")] + CreateExe(CreateExe), + /// Get various configuration information needed /// to compile programs which use Wasmer #[structopt(name = "config")] @@ -53,6 +60,8 @@ impl WasmerCLIOptions { Self::Cache(cache) => cache.execute(), Self::Validate(validate) => validate.execute(), Self::Compile(compile) => compile.execute(), + #[cfg(feature = "object-file")] + Self::CreateExe(create_exe) => create_exe.execute(), Self::Config(config) => config.execute(), Self::Inspect(inspect) => inspect.execute(), #[cfg(feature = "wast")] diff --git a/lib/cli/src/commands.rs b/lib/cli/src/commands.rs index fe7036e36..4d07c37e8 100644 --- a/lib/cli/src/commands.rs +++ b/lib/cli/src/commands.rs @@ -2,6 +2,8 @@ mod cache; mod compile; mod config; +#[cfg(feature = "object-file")] +mod create_exe; mod inspect; mod run; mod self_update; @@ -9,6 +11,8 @@ mod validate; #[cfg(feature = "wast")] mod wast; +#[cfg(feature = "object-file")] +pub use create_exe::*; #[cfg(feature = "wast")] pub use wast::*; pub use {cache::*, compile::*, config::*, inspect::*, run::*, self_update::*, validate::*}; diff --git a/lib/cli/src/commands/compile.rs b/lib/cli/src/commands/compile.rs index 1e77a08f2..bd70d6ffc 100644 --- a/lib/cli/src/commands/compile.rs +++ b/lib/cli/src/commands/compile.rs @@ -38,8 +38,7 @@ impl Compile { .context(format!("failed to compile `{}`", self.path.display())) } - fn get_recommend_extension( - &self, + pub(crate) fn get_recommend_extension( engine_type: &EngineType, target_triple: &Triple, ) -> &'static str { @@ -82,7 +81,7 @@ impl Compile { .file_stem() .map(|osstr| osstr.to_string_lossy().to_string()) .unwrap_or_default(); - let recommended_extension = self.get_recommend_extension(&engine_type, target.triple()); + let recommended_extension = Self::get_recommend_extension(&engine_type, target.triple()); match self.output.extension() { Some(ext) => { if ext != recommended_extension { diff --git a/lib/cli/src/commands/create_exe.rs b/lib/cli/src/commands/create_exe.rs new file mode 100644 index 000000000..0238da708 --- /dev/null +++ b/lib/cli/src/commands/create_exe.rs @@ -0,0 +1,273 @@ +//! 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"); + +// TODO: to get this working locally I had to add wasmer_wasm.h and wasm.h to `.wasmer/include` manually. +// this needs to be fixed before we can ship this. + +#[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, + + #[structopt(flatten)] + compiler: CompilerOptions, + + #[structopt(short = "m", multiple = true)] + cpu_features: Vec, + + /// Additional libraries to link against. + /// This is useful for fixing linker errors that may occur on some systems. + #[structopt(short = "l", multiple = true)] + libraries: Vec, +} + +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::ObjectFile; + 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)?; + + // TODO: encapsulate compile code + + #[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 header_file_path = Path::new("my_wasm.h"); + 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_object_file::ObjectFileArtifact = + module.artifact().as_ref().downcast_ref().context( + "Engine type is ObjectFile but could not downcast artifact into ObjectFileArtifact", + )?; + let symbol_registry = artifact.symbol_registry(); + let metadata_length = artifact.metadata_length(); + let module_info = module.info(); + let header_file_src = crate::c_gen::object_file_header::generate_header_file( + module_info, + symbol_registry, + metadata_length, + ); + + let header_path = header_file_path.clone(); + // 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(header_file_src.as_bytes())?; + + // auto compilation + // + + // 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")?; + // TODO: + c_src_file.write_all(WASMER_MAIN_C_SOURCE)?; + } + run_c_compile(&c_src_path, &c_src_obj).context("Failed to compile C source code")?; + LinkCode { + object_paths: vec![c_src_obj, wasm_object_path], + output_path, + additional_libraries: self.libraries.clone(), + ..Default::default() + } + .run() + .context("Failed to link objects together")?; + + eprintln!( + "✔ Native executable compiled successfully to `{}`.", + self.output.display(), + ); + + Ok(()) + } +} + +fn get_wasmer_include_directory() -> anyhow::Result { + let mut path = PathBuf::from(env::var("WASMER_DIR")?); + path.push("include"); + Ok(path) +} + +fn get_libwasmer_path() -> anyhow::Result { + let mut path = PathBuf::from(env::var("WASMER_DIR")?); + path.push("lib"); + + #[cfg(not(windows))] + path.push("libwasmer.a"); + #[cfg(windows)] + path.push("libwasmer.lib"); + + Ok(path) +} + +/// Compile the C code. +fn run_c_compile(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) + .arg("-O2") + .arg("-c") + .arg(path_to_c_src) + .arg("-I") + .arg(get_wasmer_include_directory()?) + .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, + /// Additional libraries to link against. + additional_libraries: Vec, + /// Path to the output target. + output_path: PathBuf, + /// Path to the static libwasmer library. + libwasmer_path: PathBuf, +} + +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(), + } + } +} + +impl LinkCode { + // TODO: `wasmer create-exe` needs a command line flag for extra libraries to link aganist + // or perhaps we just want to add a flag for passing things to the linker + 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")?, + ); + #[cfg(windows)] + let command = command.arg("-luserenv").arg("-lWs2_32").arg("-ladvapi32"); + #[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(()) + } +} diff --git a/lib/cli/src/commands/wasmer_create_exe_main.c b/lib/cli/src/commands/wasmer_create_exe_main.c new file mode 100644 index 000000000..ec00f9179 --- /dev/null +++ b/lib/cli/src/commands/wasmer_create_exe_main.c @@ -0,0 +1,89 @@ +#ifdef __cplusplus +extern "C" { +#endif + +#include "wasmer_wasm.h" +#include "wasm.h" +#include "my_wasm.h" + +#include +#include + +#ifdef __cplusplus +} +#endif + +void print_wasmer_error() +{ + int error_len = wasmer_last_error_length(); + 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); +} + +int main() { + printf("Initializing...\n"); + wasm_config_t* config = wasm_config_new(); + wasm_config_set_engine(config, OBJECT_FILE); + wasm_engine_t* engine = wasm_engine_new_with_config(config); + wasm_store_t* store = wasm_store_new(engine); + + wasm_module_t* module = wasmer_object_file_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); + 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_env_set_instance(wasi_env, instance); + + // WASI is now set up. + + void* vmctx = wasm_instance_get_vmctx_ptr(instance); + wasm_val_t* inout[2] = { NULL, NULL }; + + fflush(stdout); + // We're able to call our compiled function directly through a trampoline. + wasmer_trampoline_function_call__1(vmctx, wasmer_function__1, &inout); + + wasm_instance_delete(instance); + wasm_module_delete(module); + wasm_store_delete(store); + wasm_engine_delete(engine); + return 0; +} diff --git a/lib/cli/src/store.rs b/lib/cli/src/store.rs index 795fcf792..9404ac2bc 100644 --- a/lib/cli/src/store.rs +++ b/lib/cli/src/store.rs @@ -14,8 +14,27 @@ use wasmer::*; use wasmer_compiler::CompilerConfig; #[derive(Debug, Clone, StructOpt)] -/// The compiler options +/// The compiler and engine options pub struct StoreOptions { + #[structopt(flatten)] + compiler: CompilerOptions, + + /// Use JIT Engine. + #[structopt(long, conflicts_with_all = &["native", "object_file"])] + jit: bool, + + /// Use Native Engine. + #[structopt(long, conflicts_with_all = &["jit", "object_file"])] + native: bool, + + /// Use ObjectFile Engine. + #[structopt(long, conflicts_with_all = &["jit", "native"])] + object_file: bool, +} + +#[derive(Debug, Clone, StructOpt)] +/// The compiler options +pub struct CompilerOptions { /// Use Singlepass compiler. #[structopt(long, conflicts_with_all = &["cranelift", "llvm", "backend"])] singlepass: bool, @@ -36,19 +55,7 @@ pub struct StoreOptions { #[structopt(long, parse(from_os_str))] llvm_debug_dir: Option, - /// Use JIT Engine. - #[structopt(long, conflicts_with_all = &["native", "object_file"])] - jit: bool, - - /// Use Native Engine. - #[structopt(long, conflicts_with_all = &["jit", "object_file"])] - native: bool, - - /// Use ObjectFile Engine. - #[structopt(long, conflicts_with_all = &["jit", "native"])] - object_file: bool, - - /// The deprecated backend flag - Please not use + /// The deprecated backend flag - Please do not use #[structopt(long = "backend", hidden = true, conflicts_with_all = &["singlepass", "cranelift", "llvm"])] backend: Option, @@ -56,80 +63,8 @@ pub struct StoreOptions { features: WasmFeatures, } -/// The compiler used for the store -#[derive(Debug, PartialEq, Eq)] -pub enum CompilerType { - /// Singlepass compiler - Singlepass, - /// Cranelift compiler - Cranelift, - /// LLVM compiler - LLVM, - /// Headless compiler - Headless, -} - -impl CompilerType { - /// Return all enabled compilers - pub fn enabled() -> Vec { - vec![ - #[cfg(feature = "singlepass")] - Self::Singlepass, - #[cfg(feature = "cranelift")] - Self::Cranelift, - #[cfg(feature = "llvm")] - Self::LLVM, - ] - } -} - -impl ToString for CompilerType { - fn to_string(&self) -> String { - match self { - Self::Singlepass => "singlepass".to_string(), - Self::Cranelift => "cranelift".to_string(), - Self::LLVM => "llvm".to_string(), - Self::Headless => "headless".to_string(), - } - } -} - -impl FromStr for CompilerType { - type Err = Error; - fn from_str(s: &str) -> Result { - match s { - "singlepass" => Ok(Self::Singlepass), - "cranelift" => Ok(Self::Cranelift), - "llvm" => Ok(Self::LLVM), - "headless" => Ok(Self::Headless), - backend => bail!("The `{}` compiler does not exist.", backend), - } - } -} - -/// The engine used for the store -#[derive(Debug, PartialEq, Eq)] -pub enum EngineType { - /// JIT Engine - JIT, - /// Native Engine - Native, - /// Object File Engine - ObjectFile, -} - -impl ToString for EngineType { - fn to_string(&self) -> String { - match self { - Self::JIT => "jit".to_string(), - Self::Native => "native".to_string(), - Self::ObjectFile => "objectfile".to_string(), - } - } -} - -#[cfg(all(feature = "compiler", feature = "engine"))] -impl StoreOptions { +#[cfg(feature = "compiler")] +impl CompilerOptions { fn get_compiler(&self) -> Result { if self.cranelift { Ok(CompilerType::Cranelift) @@ -161,7 +96,7 @@ impl StoreOptions { } } - /// Get the Target architecture + /// Get the enaled Wasm features. pub fn get_features(&self, mut features: Features) -> Result { if self.features.threads || self.features.all { features.threads(true); @@ -181,6 +116,63 @@ impl StoreOptions { Ok(features) } + /// Gets the Store for a given target and engine. + pub fn get_store_for_target_and_engine( + &self, + target: Target, + engine_type: EngineType, + ) -> Result<(Store, CompilerType)> { + let (compiler_config, compiler_type) = self.get_compiler_config()?; + let engine = self.get_engine_by_type(target, compiler_config, engine_type)?; + let store = Store::new(&*engine); + Ok((store, compiler_type)) + } + + fn get_engine_by_type( + &self, + target: Target, + compiler_config: Box, + engine_type: EngineType, + ) -> Result> { + let features = self.get_features(compiler_config.default_features_for_target(&target))?; + let engine: Box = match engine_type { + #[cfg(feature = "jit")] + EngineType::JIT => Box::new( + wasmer_engine_jit::JIT::new(&*compiler_config) + .features(features) + .target(target) + .engine(), + ), + #[cfg(feature = "native")] + EngineType::Native => { + let mut compiler_config = compiler_config; + Box::new( + wasmer_engine_native::Native::new(&mut *compiler_config) + .target(target) + .features(features) + .engine(), + ) + } + #[cfg(feature = "object-file")] + EngineType::ObjectFile => { + let mut compiler_config = compiler_config; + Box::new( + wasmer_engine_object_file::ObjectFile::new(&mut *compiler_config) + .target(target) + .features(features) + .engine(), + ) + } + #[cfg(not(all(feature = "jit", feature = "native", feature = "object-file")))] + engine => bail!( + "The `{}` engine is not included in this binary.", + engine.to_string() + ), + }; + + Ok(engine) + } + /// Get the Compiler Config for the current options #[allow(unused_variables)] pub(crate) fn get_compiler_config(&self) -> Result<(Box, CompilerType)> { @@ -315,7 +307,82 @@ impl StoreOptions { #[allow(unreachable_code)] Ok((compiler_config, compiler)) } +} +/// The compiler used for the store +#[derive(Debug, PartialEq, Eq)] +pub enum CompilerType { + /// Singlepass compiler + Singlepass, + /// Cranelift compiler + Cranelift, + /// LLVM compiler + LLVM, + /// Headless compiler + Headless, +} + +impl CompilerType { + /// Return all enabled compilers + pub fn enabled() -> Vec { + vec![ + #[cfg(feature = "singlepass")] + Self::Singlepass, + #[cfg(feature = "cranelift")] + Self::Cranelift, + #[cfg(feature = "llvm")] + Self::LLVM, + ] + } +} + +impl ToString for CompilerType { + fn to_string(&self) -> String { + match self { + Self::Singlepass => "singlepass".to_string(), + Self::Cranelift => "cranelift".to_string(), + Self::LLVM => "llvm".to_string(), + Self::Headless => "headless".to_string(), + } + } +} + +impl FromStr for CompilerType { + type Err = Error; + fn from_str(s: &str) -> Result { + match s { + "singlepass" => Ok(Self::Singlepass), + "cranelift" => Ok(Self::Cranelift), + "llvm" => Ok(Self::LLVM), + "headless" => Ok(Self::Headless), + backend => bail!("The `{}` compiler does not exist.", backend), + } + } +} + +/// The engine used for the store +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum EngineType { + /// JIT Engine + JIT, + /// Native Engine + Native, + /// Object File Engine + ObjectFile, +} + +impl ToString for EngineType { + fn to_string(&self) -> String { + match self { + Self::JIT => "jit".to_string(), + Self::Native => "native".to_string(), + Self::ObjectFile => "objectfile".to_string(), + } + } +} + +#[cfg(all(feature = "compiler", feature = "engine"))] +impl StoreOptions { /// Gets the store for the host target, with the engine name and compiler name selected pub fn get_store(&self) -> Result<(Store, EngineType, CompilerType)> { let target = Target::default(); @@ -327,7 +394,7 @@ impl StoreOptions { &self, target: Target, ) -> Result<(Store, EngineType, CompilerType)> { - let (compiler_config, compiler_type) = self.get_compiler_config()?; + let (compiler_config, compiler_type) = self.compiler.get_compiler_config()?; let (engine, engine_type) = self.get_engine_with_compiler(target, compiler_config)?; let store = Store::new(&*engine); Ok((store, engine_type, compiler_type)) @@ -339,41 +406,10 @@ impl StoreOptions { compiler_config: Box, ) -> Result<(Box, EngineType)> { let engine_type = self.get_engine()?; - let features = self.get_features(compiler_config.default_features_for_target(&target))?; - let engine: Box = match engine_type { - #[cfg(feature = "jit")] - EngineType::JIT => Box::new( - wasmer_engine_jit::JIT::new(&*compiler_config) - .features(features) - .target(target) - .engine(), - ), - #[cfg(feature = "native")] - EngineType::Native => { - let mut compiler_config = compiler_config; - Box::new( - wasmer_engine_native::Native::new(&mut *compiler_config) - .target(target) - .features(features) - .engine(), - ) - } - #[cfg(feature = "object-file")] - EngineType::ObjectFile => { - let mut compiler_config = compiler_config; - Box::new( - wasmer_engine_object_file::ObjectFile::new(&mut *compiler_config) - .target(target) - .features(features) - .engine(), - ) - } - #[cfg(not(all(feature = "jit", feature = "native", feature = "object-file")))] - engine => bail!( - "The `{}` engine is not included in this binary.", - engine.to_string() - ), - }; + let engine = self + .compiler + .get_engine_by_type(target, compiler_config, engine_type)?; + Ok((engine, engine_type)) } } From 4b665adc7229a810f4cb83a27e78fe75d8924ac4 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 8 Oct 2020 16:33:41 -0700 Subject: [PATCH 03/49] Add test for `wasmer create-exe` --- tests/integration/cli/src/assets.rs | 32 ++++ tests/integration/cli/src/lib.rs | 8 +- tests/integration/cli/src/link_code.rs | 66 ++++++++ tests/integration/cli/src/util.rs | 55 ++++++ .../integration/cli/{src => tests}/compile.rs | 159 +----------------- tests/integration/cli/tests/create_exe.rs | 91 ++++++++++ .../object_file_engine_test_c_source.c | 0 7 files changed, 257 insertions(+), 154 deletions(-) create mode 100644 tests/integration/cli/src/assets.rs create mode 100644 tests/integration/cli/src/link_code.rs create mode 100644 tests/integration/cli/src/util.rs rename tests/integration/cli/{src => tests}/compile.rs (50%) create mode 100644 tests/integration/cli/tests/create_exe.rs rename tests/integration/cli/{src => tests}/object_file_engine_test_c_source.c (100%) diff --git a/tests/integration/cli/src/assets.rs b/tests/integration/cli/src/assets.rs new file mode 100644 index 000000000..4113e0b5c --- /dev/null +++ b/tests/integration/cli/src/assets.rs @@ -0,0 +1,32 @@ +use std::env; +use std::path::PathBuf; + +pub const ASSET_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets"); + +pub const WASMER_PATH: &str = concat!( + env!("CARGO_MANIFEST_DIR"), + "/../../../target/release/wasmer" +); + +#[cfg(not(windows))] +pub const LIBWASMER_PATH: &str = concat!( + env!("CARGO_MANIFEST_DIR"), + "/../../../target/release/libwasmer_c_api.a" +); +#[cfg(windows)] +pub const LIBWASMER_PATH: &str = concat!( + env!("CARGO_MANIFEST_DIR"), + "/../../../target/release/wasmer_c_api.lib" +); + +/// Get the path to the `libwasmer.a` static library. +pub fn get_libwasmer_path() -> PathBuf { + PathBuf::from( + env::var("WASMER_TEST_LIBWASMER_PATH").unwrap_or_else(|_| LIBWASMER_PATH.to_string()), + ) +} + +/// Get the path to the `wasmer` executable to be used in this test. +pub fn get_wasmer_path() -> PathBuf { + PathBuf::from(env::var("WASMER_TEST_WASMER_PATH").unwrap_or_else(|_| WASMER_PATH.to_string())) +} diff --git a/tests/integration/cli/src/lib.rs b/tests/integration/cli/src/lib.rs index 69d222b5e..43d6ada6a 100644 --- a/tests/integration/cli/src/lib.rs +++ b/tests/integration/cli/src/lib.rs @@ -1,6 +1,10 @@ #![forbid(unsafe_code)] -#![cfg(test)] //! CLI integration tests -mod compile; +pub mod assets; +pub mod link_code; +pub mod util; + +pub use assets::*; +pub use util::*; diff --git a/tests/integration/cli/src/link_code.rs b/tests/integration/cli/src/link_code.rs new file mode 100644 index 000000000..72fb18e1b --- /dev/null +++ b/tests/integration/cli/src/link_code.rs @@ -0,0 +1,66 @@ +use crate::assets::*; +use anyhow::bail; +use std::path::PathBuf; +use std::process::Command; + +/// Data used to run a linking command for generated artifacts. +#[derive(Debug)] +pub struct LinkCode { + /// Path to the linker used to run the linking command. + pub linker_path: PathBuf, + /// String used as an optimization flag. + pub optimization_flag: String, + /// Paths of objects to link. + pub object_paths: Vec, + /// Path to the output target. + pub output_path: PathBuf, + /// Path to the static libwasmer library. + pub libwasmer_path: PathBuf, +} + +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![], + output_path: PathBuf::from("a.out"), + libwasmer_path: get_libwasmer_path(), + } + } +} + +impl LinkCode { + pub 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()?); + #[cfg(windows)] + let command = command.arg("-luserenv").arg("-lWs2_32").arg("-ladvapi32"); + #[cfg(not(windows))] + // TODO: remove `-lffi` before shipping + let command = command.arg("-ldl").arg("-lm").arg("-pthread").arg("-lffi"); + 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(()) + } +} diff --git a/tests/integration/cli/src/util.rs b/tests/integration/cli/src/util.rs new file mode 100644 index 000000000..0e4864a67 --- /dev/null +++ b/tests/integration/cli/src/util.rs @@ -0,0 +1,55 @@ +use anyhow::bail; +use std::path::Path; +use std::process::Command; + +#[derive(Debug, Copy, Clone)] +pub enum Compiler { + Cranelift, + LLVM, + Singlepass, +} + +impl Compiler { + pub const fn to_flag(self) -> &'static str { + match self { + Compiler::Cranelift => "--cranelift", + Compiler::LLVM => "--llvm", + Compiler::Singlepass => "--singlepass", + } + } +} + +#[derive(Debug, Copy, Clone)] +pub enum Engine { + Jit, + Native, + ObjectFile, +} + +impl Engine { + pub const fn to_flag(self) -> &'static str { + match self { + Engine::Jit => "--jit", + Engine::Native => "--native", + Engine::ObjectFile => "--object-file", + } + } +} + +pub fn run_code(executable_path: &Path) -> anyhow::Result { + let output = Command::new(executable_path.canonicalize()?).output()?; + + if !output.status.success() { + bail!( + "running executable failed: 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") + ); + } + let output = + std::str::from_utf8(&output.stdout).expect("output from running executable is not utf-8"); + + Ok(output.to_owned()) +} diff --git a/tests/integration/cli/src/compile.rs b/tests/integration/cli/tests/compile.rs similarity index 50% rename from tests/integration/cli/src/compile.rs rename to tests/integration/cli/tests/compile.rs index b14635b37..ff9a7ec68 100644 --- a/tests/integration/cli/src/compile.rs +++ b/tests/integration/cli/tests/compile.rs @@ -1,84 +1,18 @@ //! CLI tests for the compile subcommand. use anyhow::{bail, Context}; -use std::env; use std::fs; use std::io::Write; use std::path::{Path, PathBuf}; use std::process::Command; - -const CLI_INTEGRATION_TESTS_ASSETS: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets"); +use wasmer_integration_tests_cli::link_code::*; +use wasmer_integration_tests_cli::*; const OBJECT_FILE_ENGINE_TEST_C_SOURCE: &[u8] = include_bytes!("object_file_engine_test_c_source.c"); -// TODO: -const OBJECT_FILE_ENGINE_TEST_WASM_PATH: &str = - concat!(env!("CARGO_MANIFEST_DIR"), "/assets/qjs.wasm"); -const WASMER_PATH: &str = concat!( - env!("CARGO_MANIFEST_DIR"), - "/../../../target/release/wasmer" -); - -#[cfg(not(windows))] -const LIBWASMER_PATH: &str = concat!( - env!("CARGO_MANIFEST_DIR"), - "/../../../target/release/libwasmer_c_api.a" -); -#[cfg(windows)] -const LIBWASMER_PATH: &str = concat!( - env!("CARGO_MANIFEST_DIR"), - "/../../../target/release/wasmer_c_api.lib" -); - -/// Get the path to the `wasmer` executable to be used in this test. -fn get_wasmer_path() -> PathBuf { - PathBuf::from(env::var("WASMER_TEST_WASMER_PATH").unwrap_or_else(|_| WASMER_PATH.to_string())) -} - -/// Get the path to the `libwasmer.a` static library. -fn get_libwasmer_path() -> PathBuf { - PathBuf::from( - env::var("WASMER_TEST_LIBWASMER_PATH").unwrap_or_else(|_| LIBWASMER_PATH.to_string()), - ) -} - -#[allow(dead_code)] -#[derive(Debug, Copy, Clone)] -pub enum Engine { - Jit, - Native, - ObjectFile, -} - -impl Engine { - // TODO: make this `const fn` when Wasmer moves to Rust 1.46.0+ - pub fn to_flag(self) -> &'static str { - match self { - Engine::Jit => "--jit", - Engine::Native => "--native", - Engine::ObjectFile => "--object-file", - } - } -} - -#[allow(dead_code)] -#[derive(Debug, Copy, Clone)] -pub enum Compiler { - Cranelift, - LLVM, - Singlepass, -} - -impl Compiler { - // TODO: make this `const fn` when Wasmer moves to Rust 1.46.0+ - pub fn to_flag(self) -> &'static str { - match self { - Compiler::Cranelift => "--cranelift", - Compiler::LLVM => "--llvm", - Compiler::Singlepass => "--singlepass", - } - } +fn object_file_engine_test_wasm_path() -> String { + format!("{}/{}", ASSET_PATH, "qjs.wasm") } /// Data used to run the `wasmer compile` command. @@ -106,7 +40,7 @@ impl Default for WasmerCompile { let wasm_obj_path = "wasm.obj"; Self { wasmer_path: get_wasmer_path(), - wasm_path: PathBuf::from(OBJECT_FILE_ENGINE_TEST_WASM_PATH), + wasm_path: PathBuf::from(object_file_engine_test_wasm_path()), wasm_object_path: PathBuf::from(wasm_obj_path), header_output_path: PathBuf::from("my_wasm.h"), compiler: Compiler::Cranelift, @@ -153,7 +87,7 @@ fn run_c_compile(path_to_c_src: &Path, output_name: &Path) -> anyhow::Result<()> .arg("-c") .arg(path_to_c_src) .arg("-I") - .arg(CLI_INTEGRATION_TESTS_ASSETS) + .arg(ASSET_PATH) .arg("-o") .arg(output_name) .output()?; @@ -170,92 +104,13 @@ fn run_c_compile(path_to_c_src: &Path, output_name: &Path) -> anyhow::Result<()> 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, - /// Path to the output target. - output_path: PathBuf, - /// Path to the static libwasmer library. - libwasmer_path: PathBuf, -} - -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![], - output_path: PathBuf::from("a.out"), - libwasmer_path: get_libwasmer_path(), - } - } -} - -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()?); - #[cfg(windows)] - let command = command.arg("-luserenv").arg("-lWs2_32").arg("-ladvapi32"); - #[cfg(not(windows))] - let command = command.arg("-ldl").arg("-lm").arg("-pthread"); - 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(()) - } -} - -fn run_code(executable_path: &Path) -> anyhow::Result { - let output = Command::new(executable_path.canonicalize()?).output()?; - - if !output.status.success() { - bail!( - "running executable failed: 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") - ); - } - let output = - std::str::from_utf8(&output.stdout).expect("output from running executable is not utf-8"); - - Ok(output.to_owned()) -} - #[test] fn object_file_engine_works() -> anyhow::Result<()> { let operating_dir = tempfile::tempdir()?; std::env::set_current_dir(&operating_dir)?; - let wasm_path = PathBuf::from(OBJECT_FILE_ENGINE_TEST_WASM_PATH); + let wasm_path = PathBuf::from(object_file_engine_test_wasm_path()); #[cfg(not(windows))] let wasm_object_path = PathBuf::from("wasm.o"); #[cfg(windows)] diff --git a/tests/integration/cli/tests/create_exe.rs b/tests/integration/cli/tests/create_exe.rs new file mode 100644 index 000000000..d2f50a6a9 --- /dev/null +++ b/tests/integration/cli/tests/create_exe.rs @@ -0,0 +1,91 @@ +//! Tests of the `wasmer create-exe` command. + +use anyhow::{bail, Context}; +use std::path::PathBuf; +use std::process::Command; +use wasmer_integration_tests_cli::*; + +fn create_exe_test_wasm_path() -> String { + format!("{}/{}", ASSET_PATH, "qjs.wasm") +} + +/// Data used to run the `wasmer compile` command. +#[derive(Debug)] +struct WasmerCreateExe { + /// 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 native executable produced by compiling the Wasm. + native_executable_path: PathBuf, + /// Compiler with which to compile the Wasm. + compiler: Compiler, +} + +impl Default for WasmerCreateExe { + fn default() -> Self { + #[cfg(not(windows))] + let native_executable_path = PathBuf::from("wasm.out"); + #[cfg(windows)] + let native_executable_path = PathBuf::from("wasm.exe"); + Self { + wasmer_path: get_wasmer_path(), + wasm_path: PathBuf::from(create_exe_test_wasm_path()), + native_executable_path, + compiler: Compiler::Cranelift, + } + } +} + +impl WasmerCreateExe { + fn run(&self) -> anyhow::Result<()> { + let output = Command::new(&self.wasmer_path) + .arg("create-exe") + .arg(&self.wasm_path.canonicalize()?) + .arg(&self.compiler.to_flag()) + // TODO: remove before shipping + .arg("-lffi") + .arg("-o") + .arg(&self.native_executable_path) + .output()?; + + if !output.status.success() { + bail!( + "wasmer create-exe 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 create_exe_works() -> anyhow::Result<()> { + let operating_dir = tempfile::tempdir()?; + + std::env::set_current_dir(&operating_dir)?; + + let wasm_path = PathBuf::from(create_exe_test_wasm_path()); + #[cfg(not(windows))] + let executable_path = PathBuf::from("wasm.out"); + #[cfg(windows)] + let executable_path = PathBuf::from("wasm.exe"); + + WasmerCreateExe { + wasm_path: wasm_path.clone(), + native_executable_path: executable_path.clone(), + compiler: Compiler::Cranelift, + ..Default::default() + } + .run() + .context("Failed to create-exe wasm with Wasmer")?; + + let result = run_code(&executable_path).context("Failed to run generated executable")?; + let result_lines = result.lines().collect::>(); + assert_eq!(result_lines, vec!["Initializing...", "\"Hello, World\""],); + + Ok(()) +} diff --git a/tests/integration/cli/src/object_file_engine_test_c_source.c b/tests/integration/cli/tests/object_file_engine_test_c_source.c similarity index 100% rename from tests/integration/cli/src/object_file_engine_test_c_source.c rename to tests/integration/cli/tests/object_file_engine_test_c_source.c From ba7b24f696228555603c1ac78da85b2d5bedc945 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 9 Oct 2020 17:38:14 -0700 Subject: [PATCH 04/49] Add --dir and --mapdir support to native executable --- lib/c-api/build.rs | 2 + lib/c-api/src/wasm_c_api/wasi/mod.rs | 57 +++++ lib/c-api/wasmer_wasm.h | 8 + lib/cli/src/commands/wasmer_create_exe_main.c | 223 ++++++++++++------ tests/integration/cli/src/util.rs | 6 +- tests/integration/cli/tests/compile.rs | 2 +- tests/integration/cli/tests/create_exe.rs | 6 +- 7 files changed, 231 insertions(+), 73 deletions(-) diff --git a/lib/c-api/build.rs b/lib/c-api/build.rs index e78874382..f9f78ec10 100644 --- a/lib/c-api/build.rs +++ b/lib/c-api/build.rs @@ -357,6 +357,8 @@ fn exclude_items_from_wasm_c_api(builder: Builder) -> Builder { builder .exclude_item("wasi_config_arg") .exclude_item("wasi_config_env") + .exclude_item("wasi_config_mapdir") + .exclude_item("wasi_config_preopen_dir") .exclude_item("wasi_config_inherit_stderr") .exclude_item("wasi_config_inherit_stdin") .exclude_item("wasi_config_inherit_stdout") diff --git a/lib/c-api/src/wasm_c_api/wasi/mod.rs b/lib/c-api/src/wasm_c_api/wasi/mod.rs index cf0a08b73..fb0047c73 100644 --- a/lib/c-api/src/wasm_c_api/wasi/mod.rs +++ b/lib/c-api/src/wasm_c_api/wasi/mod.rs @@ -75,6 +75,63 @@ pub unsafe extern "C" fn wasi_config_arg(config: &mut wasi_config_t, arg: *const config.state_builder.arg(arg_bytes); } +#[no_mangle] +pub unsafe extern "C" fn wasi_config_preopen_dir( + config: &mut wasi_config_t, + dir: *const c_char, +) -> bool { + let dir_cstr = CStr::from_ptr(dir); + let dir_bytes = dir_cstr.to_bytes(); + let dir_str = match std::str::from_utf8(dir_bytes) { + Ok(dir_str) => dir_str, + Err(e) => { + update_last_error(e); + return false; + } + }; + + if let Err(e) = config.state_builder.preopen_dir(dir_str) { + update_last_error(e); + return false; + } + + true +} + +#[no_mangle] +pub unsafe extern "C" fn wasi_config_mapdir( + config: &mut wasi_config_t, + alias: *const c_char, + dir: *const c_char, +) -> bool { + let alias_cstr = CStr::from_ptr(alias); + let alias_bytes = alias_cstr.to_bytes(); + let alias_str = match std::str::from_utf8(alias_bytes) { + Ok(alias_str) => alias_str, + Err(e) => { + update_last_error(e); + return false; + } + }; + + let dir_cstr = CStr::from_ptr(dir); + let dir_bytes = dir_cstr.to_bytes(); + let dir_str = match std::str::from_utf8(dir_bytes) { + Ok(dir_str) => dir_str, + Err(e) => { + update_last_error(e); + return false; + } + }; + + if let Err(e) = config.state_builder.map_dir(alias_str, dir_str) { + update_last_error(e); + return false; + } + + true +} + #[no_mangle] pub extern "C" fn wasi_config_inherit_stdout(config: &mut wasi_config_t) { config.inherit_stdout = true; diff --git a/lib/c-api/wasmer_wasm.h b/lib/c-api/wasmer_wasm.h index b9eea99ae..e8f139ee8 100644 --- a/lib/c-api/wasmer_wasm.h +++ b/lib/c-api/wasmer_wasm.h @@ -101,10 +101,18 @@ void wasi_config_inherit_stdin(wasi_config_t *config); void wasi_config_inherit_stdout(wasi_config_t *config); #endif +#if defined(WASMER_WASI_ENABLED) +bool wasi_config_mapdir(wasi_config_t *config, const char *alias, const char *dir); +#endif + #if defined(WASMER_WASI_ENABLED) wasi_config_t *wasi_config_new(const char *program_name); #endif +#if defined(WASMER_WASI_ENABLED) +bool wasi_config_preopen_dir(wasi_config_t *config, const char *dir); +#endif + #if defined(WASMER_WASI_ENABLED) void wasi_env_delete(wasi_env_t *_state); #endif diff --git a/lib/cli/src/commands/wasmer_create_exe_main.c b/lib/cli/src/commands/wasmer_create_exe_main.c index ec00f9179..6b56c02fc 100644 --- a/lib/cli/src/commands/wasmer_create_exe_main.c +++ b/lib/cli/src/commands/wasmer_create_exe_main.c @@ -9,81 +9,166 @@ extern "C" { #include #include +// TODO: make this define templated so that the Rust code can toggle it on/off +#define WASI + #ifdef __cplusplus } #endif void print_wasmer_error() { - int error_len = wasmer_last_error_length(); - 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); + int error_len = wasmer_last_error_length(); + 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); } -int main() { - printf("Initializing...\n"); - wasm_config_t* config = wasm_config_new(); - wasm_config_set_engine(config, OBJECT_FILE); - wasm_engine_t* engine = wasm_engine_new_with_config(config); - wasm_store_t* store = wasm_store_new(engine); - - wasm_module_t* module = wasmer_object_file_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); - 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_env_set_instance(wasi_env, instance); - - // WASI is now set up. - - void* vmctx = wasm_instance_get_vmctx_ptr(instance); - wasm_val_t* inout[2] = { NULL, NULL }; - - fflush(stdout); - // We're able to call our compiled function directly through a trampoline. - wasmer_trampoline_function_call__1(vmctx, wasmer_function__1, &inout); - - wasm_instance_delete(instance); - wasm_module_delete(module); - wasm_store_delete(store); - wasm_engine_delete(engine); - return 0; +#ifdef WASI +int find_colon(char* string) { + int colon_location = 0; + for (int j = 0; j < strlen(string); ++j) { + if (string[j] == ':') { + colon_location = j; + break; + } + } + return colon_location; +} + +void pass_mapdir_arg(wasi_config_t* wasi_config, char* mapdir) { + int colon_location = find_colon(mapdir); + if (colon_location == 0) { + // error malformed argument + fprintf(stderr, "Expected mapdir argument of the form alias:directory\n"); + exit(-1); + } + // TODO: triple check for off by one errors + int dir_len = strlen(mapdir) - colon_location; + char* alias = (char*)malloc(colon_location); + char* dir = (char*)malloc(dir_len); + int j = 0; + for (j = 0; j < colon_location; ++j) { + alias[j] = mapdir[j]; + } + alias[j] = 0; + for (j = 0; j < dir_len; ++j) { + dir[j] = mapdir[j + colon_location]; + } + dir[j] = 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. +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[]) { + printf("Initializing...\n"); + wasm_config_t* config = wasm_config_new(); + wasm_config_set_engine(config, OBJECT_FILE); + wasm_engine_t* engine = wasm_engine_new_with_config(config); + wasm_store_t* store = wasm_store_new(engine); + + wasm_module_t* module = wasmer_object_file_engine_new(store, "qjs.wasm"); + 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); + 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); + + #ifdef WASI + bool get_imports_result = wasi_get_imports(store, module, wasi_env, imports); + 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, (const wasm_extern_t* const*) imports, NULL); + if (! instance) { + fprintf(stderr, "Failed to create instance\n"); + print_wasmer_error(); + return -1; + } + + #ifdef WASI + wasi_env_set_instance(wasi_env, instance); + #endif + + void* vmctx = wasm_instance_get_vmctx_ptr(instance); + wasm_val_t* inout[2] = { NULL, NULL }; + + // We're able to call our compiled function directly through a trampoline. + wasmer_trampoline_function_call__1(vmctx, wasmer_function__1, &inout); + + wasm_instance_delete(instance); + wasm_module_delete(module); + wasm_store_delete(store); + wasm_engine_delete(engine); + return 0; } diff --git a/tests/integration/cli/src/util.rs b/tests/integration/cli/src/util.rs index 0e4864a67..8cfcaae20 100644 --- a/tests/integration/cli/src/util.rs +++ b/tests/integration/cli/src/util.rs @@ -36,8 +36,10 @@ impl Engine { } } -pub fn run_code(executable_path: &Path) -> anyhow::Result { - let output = Command::new(executable_path.canonicalize()?).output()?; +pub fn run_code(executable_path: &Path, args: &[String]) -> anyhow::Result { + let output = Command::new(executable_path.canonicalize()?) + .args(args) + .output()?; if !output.status.success() { bail!( diff --git a/tests/integration/cli/tests/compile.rs b/tests/integration/cli/tests/compile.rs index ff9a7ec68..fcbedc938 100644 --- a/tests/integration/cli/tests/compile.rs +++ b/tests/integration/cli/tests/compile.rs @@ -153,7 +153,7 @@ fn object_file_engine_works() -> anyhow::Result<()> { .run() .context("Failed to link objects together")?; - let result = run_code(&executable_path).context("Failed to run generated executable")?; + let result = run_code(&executable_path, &[]).context("Failed to run generated executable")?; let result_lines = result.lines().collect::>(); assert_eq!(result_lines, vec!["Initializing...", "\"Hello, World\""],); diff --git a/tests/integration/cli/tests/create_exe.rs b/tests/integration/cli/tests/create_exe.rs index d2f50a6a9..2af9e390f 100644 --- a/tests/integration/cli/tests/create_exe.rs +++ b/tests/integration/cli/tests/create_exe.rs @@ -83,7 +83,11 @@ fn create_exe_works() -> anyhow::Result<()> { .run() .context("Failed to create-exe wasm with Wasmer")?; - let result = run_code(&executable_path).context("Failed to run generated executable")?; + let result = run_code( + &executable_path, + &["--eval".to_string(), "function greet(name) { return JSON.stringify('Hello, ' + name); }; print(greet('World'));".to_string()], + ) + .context("Failed to run generated executable")?; let result_lines = result.lines().collect::>(); assert_eq!(result_lines, vec!["Initializing...", "\"Hello, World\""],); From 309d1b671624b916ed4dde8762bbdf02515c68f3 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 10 Sep 2020 12:23:51 +0200 Subject: [PATCH 05/49] Add i32/i64 converters from unsigned ints --- lib/wasmer-types/src/values.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/wasmer-types/src/values.rs b/lib/wasmer-types/src/values.rs index 98389d3c1..3e774bb5d 100644 --- a/lib/wasmer-types/src/values.rs +++ b/lib/wasmer-types/src/values.rs @@ -8,10 +8,14 @@ use crate::types::Type; /// produce. #[derive(Clone, PartialEq)] pub enum Value { - /// A 32-bit integer + /// A 32-bit integer. + /// + /// In Wasm integers are sign-agnostic, i.e. this can either be signed or unsigned. I32(i32), - /// A 64-bit integer + /// A 64-bit integer. + /// + /// In Wasm integers are sign-agnostic, i.e. this can either be signed or unsigned. I64(i64), /// A 32-bit float. @@ -175,12 +179,26 @@ impl From for Value { } } +impl From for Value { + fn from(val: u32) -> Self { + // In Wasm integers are sign-agnostic, so i32 is basically a 4 byte storage we can use for signed or unsigned 32-bit integers. + Self::I32(val as i32) + } +} + impl From for Value { fn from(val: i64) -> Self { Self::I64(val) } } +impl From for Value { + fn from(val: u64) -> Self { + // In Wasm integers are sign-agnostic, so i64 is basically an 8 byte storage we can use for signed or unsigned 64-bit integers. + Self::I64(val as i64) + } +} + impl From for Value { fn from(val: f32) -> Self { Self::F32(val) From 209dbedf66a1577174f957d4099a044dfea5625e Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 16 Sep 2020 08:23:18 +0200 Subject: [PATCH 06/49] Add unit tests for Value from u32/u64 --- lib/wasmer-types/src/values.rs | 75 ++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/lib/wasmer-types/src/values.rs b/lib/wasmer-types/src/values.rs index 3e774bb5d..4f37a7128 100644 --- a/lib/wasmer-types/src/values.rs +++ b/lib/wasmer-types/src/values.rs @@ -222,3 +222,78 @@ impl From for Value { // Self::FuncRef(val) // } // } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_value_i32_from_u32() { + let input: u32 = 0x00000000; + let value = Value::<()>::from(input); + let mut out: i128 = 0; + unsafe { + value.write_value_to(&mut out as *mut i128); + } + assert_eq!(out, 0x00000000); + + let input: u32 = 0x00000001; + let value = Value::<()>::from(input); + let mut out: i128 = 0; + unsafe { + value.write_value_to(&mut out as *mut i128); + } + assert_eq!(out, 0x00000001); + + let input: u32 = 0xaabbccdd; + let value = Value::<()>::from(input); + let mut out: i128 = 0; + unsafe { + value.write_value_to(&mut out as *mut i128); + } + assert_eq!(out, 0xaabbccdd); + + let input: u32 = 0xffffffff; + let value = Value::<()>::from(input); + let mut out: i128 = 0; + unsafe { + value.write_value_to(&mut out as *mut i128); + } + assert_eq!(out, 0xffffffff); + } + + #[test] + fn test_value_i64_from_u64() { + let input: u64 = 0x0000000000000000; + let value = Value::<()>::from(input); + let mut out: i128 = 0; + unsafe { + value.write_value_to(&mut out as *mut i128); + } + assert_eq!(out, 0x0000000000000000); + + let input: u64 = 0x0000000000000001; + let value = Value::<()>::from(input); + let mut out: i128 = 0; + unsafe { + value.write_value_to(&mut out as *mut i128); + } + assert_eq!(out, 0x0000000000000001); + + let input: u64 = 0xaabbccddeeff0011; + let value = Value::<()>::from(input); + let mut out: i128 = 0; + unsafe { + value.write_value_to(&mut out as *mut i128); + } + assert_eq!(out, 0xaabbccddeeff0011); + + let input: u64 = 0xffffffffffffffff; + let value = Value::<()>::from(input); + let mut out: i128 = 0; + unsafe { + value.write_value_to(&mut out as *mut i128); + } + assert_eq!(out, 0xffffffffffffffff); + } +} From 5cc8439487a8d5bd62542db890fbdc1f668b8464 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Sat, 10 Oct 2020 08:36:30 +0200 Subject: [PATCH 07/49] Use from_be_bytes for tests --- lib/wasmer-types/src/values.rs | 80 ++++++++++------------------------ 1 file changed, 24 insertions(+), 56 deletions(-) diff --git a/lib/wasmer-types/src/values.rs b/lib/wasmer-types/src/values.rs index 4f37a7128..bf497701b 100644 --- a/lib/wasmer-types/src/values.rs +++ b/lib/wasmer-types/src/values.rs @@ -229,71 +229,39 @@ mod tests { #[test] fn test_value_i32_from_u32() { - let input: u32 = 0x00000000; - let value = Value::<()>::from(input); - let mut out: i128 = 0; - unsafe { - value.write_value_to(&mut out as *mut i128); - } - assert_eq!(out, 0x00000000); + let bytes = [0x00, 0x00, 0x00, 0x00]; + let v = Value::<()>::from(u32::from_be_bytes(bytes.clone())); + assert_eq!(v, Value::I32(i32::from_be_bytes(bytes.clone()))); - let input: u32 = 0x00000001; - let value = Value::<()>::from(input); - let mut out: i128 = 0; - unsafe { - value.write_value_to(&mut out as *mut i128); - } - assert_eq!(out, 0x00000001); + let bytes = [0x00, 0x00, 0x00, 0x01]; + let v = Value::<()>::from(u32::from_be_bytes(bytes.clone())); + assert_eq!(v, Value::I32(i32::from_be_bytes(bytes.clone()))); - let input: u32 = 0xaabbccdd; - let value = Value::<()>::from(input); - let mut out: i128 = 0; - unsafe { - value.write_value_to(&mut out as *mut i128); - } - assert_eq!(out, 0xaabbccdd); + let bytes = [0xAA, 0xBB, 0xCC, 0xDD]; + let v = Value::<()>::from(u32::from_be_bytes(bytes.clone())); + assert_eq!(v, Value::I32(i32::from_be_bytes(bytes.clone()))); - let input: u32 = 0xffffffff; - let value = Value::<()>::from(input); - let mut out: i128 = 0; - unsafe { - value.write_value_to(&mut out as *mut i128); - } - assert_eq!(out, 0xffffffff); + let bytes = [0xFF, 0xFF, 0xFF, 0xFF]; + let v = Value::<()>::from(u32::from_be_bytes(bytes.clone())); + assert_eq!(v, Value::I32(i32::from_be_bytes(bytes.clone()))); } #[test] fn test_value_i64_from_u64() { - let input: u64 = 0x0000000000000000; - let value = Value::<()>::from(input); - let mut out: i128 = 0; - unsafe { - value.write_value_to(&mut out as *mut i128); - } - assert_eq!(out, 0x0000000000000000); + let bytes = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + let v = Value::<()>::from(u64::from_be_bytes(bytes.clone())); + assert_eq!(v, Value::I64(i64::from_be_bytes(bytes.clone()))); - let input: u64 = 0x0000000000000001; - let value = Value::<()>::from(input); - let mut out: i128 = 0; - unsafe { - value.write_value_to(&mut out as *mut i128); - } - assert_eq!(out, 0x0000000000000001); + let bytes = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]; + let v = Value::<()>::from(u64::from_be_bytes(bytes.clone())); + assert_eq!(v, Value::I64(i64::from_be_bytes(bytes.clone()))); - let input: u64 = 0xaabbccddeeff0011; - let value = Value::<()>::from(input); - let mut out: i128 = 0; - unsafe { - value.write_value_to(&mut out as *mut i128); - } - assert_eq!(out, 0xaabbccddeeff0011); + let bytes = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11]; + let v = Value::<()>::from(u64::from_be_bytes(bytes.clone())); + assert_eq!(v, Value::I64(i64::from_be_bytes(bytes.clone()))); - let input: u64 = 0xffffffffffffffff; - let value = Value::<()>::from(input); - let mut out: i128 = 0; - unsafe { - value.write_value_to(&mut out as *mut i128); - } - assert_eq!(out, 0xffffffffffffffff); + let bytes = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; + let v = Value::<()>::from(u64::from_be_bytes(bytes.clone())); + assert_eq!(v, Value::I64(i64::from_be_bytes(bytes.clone()))); } } From 750a1c3645478616157872111ae1c6d0083a2ecd Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 12 Oct 2020 17:52:40 +0200 Subject: [PATCH 08/49] feat(c-api) Implement `wasm_module_name` and `wasm_module_set_name`. I submited a proposal to the official `wasm.h` by the way, https://github.com/WebAssembly/wasm-c-api/pull/157. For the moment, let's keep that as a vendor specific implementation. --- lib/c-api/src/wasm_c_api/wasmer.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/c-api/src/wasm_c_api/wasmer.rs b/lib/c-api/src/wasm_c_api/wasmer.rs index 5d6122c05..6b811e5f2 100644 --- a/lib/c-api/src/wasm_c_api/wasmer.rs +++ b/lib/c-api/src/wasm_c_api/wasmer.rs @@ -1,9 +1,35 @@ //! Wasmer-specific extensions to the Wasm C API. -use crate::wasm_c_api::instance::wasm_instance_t; +use super::instance::wasm_instance_t; +use super::module::wasm_module_t; +use super::types::wasm_name_t; use std::ffi::c_void; +use std::str; #[no_mangle] pub unsafe extern "C" fn wasm_instance_get_vmctx_ptr(instance: &wasm_instance_t) -> *mut c_void { instance.inner.vmctx_ptr() as _ } + +#[no_mangle] +pub unsafe extern "C" fn wasm_module_name(module: &wasm_module_t, out: &mut wasm_name_t) { + let name = match module.inner.name() { + Some(name) => name, + None => return, + }; + + *out = name.as_bytes().to_vec().into(); +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_module_set_name(module: &wasm_module_t, name: &wasm_name_t) -> bool { + let name = match name.into_slice() { + Some(name) => match str::from_utf8(name) { + Ok(name) => name, + Err(_) => return false, // not ideal! + }, + None => return false, + }; + + module.inner.set_name(name) +} From b9f98ce1f696088a9764704198c1606500895ecf Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 12 Oct 2020 17:54:21 +0200 Subject: [PATCH 09/49] feat(c-api) Update `.h` files. --- lib/c-api/build.rs | 8 +++++--- lib/c-api/wasmer_wasm.h | 4 ++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/c-api/build.rs b/lib/c-api/build.rs index 627ad9039..e43285a62 100644 --- a/lib/c-api/build.rs +++ b/lib/c-api/build.rs @@ -378,10 +378,12 @@ fn exclude_items_from_wasm_c_api(builder: Builder) -> Builder { .exclude_item("wasi_get_start_function") .exclude_item("wasi_get_wasi_version") .exclude_item("wasi_version_t") - .exclude_item("wasm_instance_get_vmctx_ptr") - .exclude_item("wasmer_compiler_t") - .exclude_item("wasmer_engine_t") .exclude_item("wasm_config_set_compiler") .exclude_item("wasm_config_set_engine") + .exclude_item("wasm_instance_get_vmctx_ptr") + .exclude_item("wasm_module_name") + .exclude_item("wasm_module_set_name") + .exclude_item("wasmer_compiler_t") + .exclude_item("wasmer_engine_t") .exclude_item("wat2wasm") } diff --git a/lib/c-api/wasmer_wasm.h b/lib/c-api/wasmer_wasm.h index b9eea99ae..024304011 100644 --- a/lib/c-api/wasmer_wasm.h +++ b/lib/c-api/wasmer_wasm.h @@ -156,6 +156,10 @@ void wasm_config_set_engine(wasm_config_t *config, wasmer_engine_t engine); void *wasm_instance_get_vmctx_ptr(const wasm_instance_t *instance); +void wasm_module_name(const wasm_module_t *module, wasm_name_t *out); + +bool wasm_module_set_name(const wasm_module_t *module, const wasm_name_t *name); + /** * Gets the length in bytes of the last error if any. * From fe70330772f8545e3f32e4c1696031c3ef2f575c Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 12 Oct 2020 18:02:32 +0200 Subject: [PATCH 10/49] fixup --- lib/c-api/src/wasm_c_api/wasmer.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/c-api/src/wasm_c_api/wasmer.rs b/lib/c-api/src/wasm_c_api/wasmer.rs index 6b811e5f2..b62a2ec1d 100644 --- a/lib/c-api/src/wasm_c_api/wasmer.rs +++ b/lib/c-api/src/wasm_c_api/wasmer.rs @@ -5,6 +5,7 @@ use super::module::wasm_module_t; use super::types::wasm_name_t; use std::ffi::c_void; use std::str; +use std::sync::Arc; #[no_mangle] pub unsafe extern "C" fn wasm_instance_get_vmctx_ptr(instance: &wasm_instance_t) -> *mut c_void { @@ -22,7 +23,10 @@ pub unsafe extern "C" fn wasm_module_name(module: &wasm_module_t, out: &mut wasm } #[no_mangle] -pub unsafe extern "C" fn wasm_module_set_name(module: &wasm_module_t, name: &wasm_name_t) -> bool { +pub unsafe extern "C" fn wasm_module_set_name( + module: &mut wasm_module_t, + name: &wasm_name_t, +) -> bool { let name = match name.into_slice() { Some(name) => match str::from_utf8(name) { Ok(name) => name, @@ -31,5 +35,8 @@ pub unsafe extern "C" fn wasm_module_set_name(module: &wasm_module_t, name: &was None => return false, }; - module.inner.set_name(name) + match Arc::get_mut(&mut module.inner) { + Some(module) => module.set_name(name), + None => false, + } } From f866ea39fd68d37dba6ac3ce431b37b58d75f130 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 12 Oct 2020 18:02:45 +0200 Subject: [PATCH 11/49] fixup --- lib/c-api/wasmer_wasm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/c-api/wasmer_wasm.h b/lib/c-api/wasmer_wasm.h index 024304011..6dffd665d 100644 --- a/lib/c-api/wasmer_wasm.h +++ b/lib/c-api/wasmer_wasm.h @@ -158,7 +158,7 @@ void *wasm_instance_get_vmctx_ptr(const wasm_instance_t *instance); void wasm_module_name(const wasm_module_t *module, wasm_name_t *out); -bool wasm_module_set_name(const wasm_module_t *module, const wasm_name_t *name); +bool wasm_module_set_name(wasm_module_t *module, const wasm_name_t *name); /** * Gets the length in bytes of the last error if any. From c6460d1d77ad4977edbeda3f48a0dc8c94e3c59e Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 12 Oct 2020 13:48:26 -0700 Subject: [PATCH 12/49] Add `target` support to `wasmer create-exe` --- lib/cli/src/commands/create_exe.rs | 37 +++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/lib/cli/src/commands/create_exe.rs b/lib/cli/src/commands/create_exe.rs index 0238da708..b8cbf85e2 100644 --- a/lib/cli/src/commands/create_exe.rs +++ b/lib/cli/src/commands/create_exe.rs @@ -130,11 +130,13 @@ impl CreateExe { // TODO: c_src_file.write_all(WASMER_MAIN_C_SOURCE)?; } - run_c_compile(&c_src_path, &c_src_obj).context("Failed to compile C source code")?; + 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() @@ -155,6 +157,7 @@ fn get_wasmer_include_directory() -> anyhow::Result { Ok(path) } +/// path to the static libwasmer fn get_libwasmer_path() -> anyhow::Result { let mut path = PathBuf::from(env::var("WASMER_DIR")?); path.push("lib"); @@ -168,21 +171,31 @@ fn get_libwasmer_path() -> anyhow::Result { } /// Compile the C code. -fn run_c_compile(path_to_c_src: &Path, output_name: &Path) -> anyhow::Result<()> { +fn run_c_compile( + path_to_c_src: &Path, + output_name: &Path, + target: Option, +) -> anyhow::Result<()> { #[cfg(not(windows))] let c_compiler = "cc"; #[cfg(windows)] let c_compiler = "clang++"; - let output = Command::new(c_compiler) + 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()?) - .arg("-o") - .arg(output_name) - .output()?; + .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!( @@ -209,8 +222,10 @@ struct LinkCode { additional_libraries: Vec, /// Path to the output target. output_path: PathBuf, - /// Path to the static libwasmer library. + /// Path to the dir containing the static libwasmer library. libwasmer_path: PathBuf, + /// The target to link the executable for. + target: Option, } impl Default for LinkCode { @@ -226,6 +241,7 @@ impl Default for LinkCode { additional_libraries: vec![], output_path: PathBuf::from("a.out"), libwasmer_path: get_libwasmer_path().unwrap(), + target: None, } } } @@ -248,6 +264,11 @@ impl LinkCode { .canonicalize() .context("Failed to find libwasmer")?, ); + let command = if let Some(target) = &self.target { + command.arg("-target").arg(format!("{}", target)) + } else { + command + }; #[cfg(windows)] let command = command.arg("-luserenv").arg("-lWs2_32").arg("-ladvapi32"); #[cfg(not(windows))] From 21c17704263d6210af1e22583263c285441ea04f Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 12 Oct 2020 13:51:03 -0700 Subject: [PATCH 13/49] Improve error message when `WASMER_DIR` is not set --- lib/cli/src/commands/create_exe.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/cli/src/commands/create_exe.rs b/lib/cli/src/commands/create_exe.rs index b8cbf85e2..3caa57944 100644 --- a/lib/cli/src/commands/create_exe.rs +++ b/lib/cli/src/commands/create_exe.rs @@ -151,15 +151,21 @@ impl CreateExe { } } +fn get_wasmer_dir() -> anyhow::Result { + Ok(PathBuf::from( + env::var("WASMER_DIR").context("Trying to read env var `WASMER_DIR`")?, + )) +} + fn get_wasmer_include_directory() -> anyhow::Result { - let mut path = PathBuf::from(env::var("WASMER_DIR")?); + let mut path = get_wasmer_dir()?; path.push("include"); Ok(path) } /// path to the static libwasmer fn get_libwasmer_path() -> anyhow::Result { - let mut path = PathBuf::from(env::var("WASMER_DIR")?); + let mut path = get_wasmer_dir()?; path.push("lib"); #[cfg(not(windows))] From fff5366e530d2c21ac4fc4912869a97a8425a855 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Mon, 12 Oct 2020 14:54:21 -0700 Subject: [PATCH 14/49] Update CHANGELOG format based on Keep a Changelog --- CHANGELOG.md | 74 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41c7a1d46..74d19d04d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,83 +1,115 @@ # Changelog +*The format is based on [Keep a Changelog].* + +[Keep a Changelog]: http://keepachangelog.com/en/1.0.0/ + + ## **[Unreleased]** +### Added + - [#1700](https://github.com/wasmerio/wasmer/pull/1700) Implement `wasm_externtype_copy` in the Wasm C API. ## 1.0.0-alpha4 - 2020-10-08 -- [#1691](https://github.com/wasmerio/wasmer/pull/1691) Bump minimum supported Rust version to 1.46.0 -- [#1690](https://github.com/wasmerio/wasmer/pull/1690) Fix `wasm_memorytype_limits` where `min` and `max` represents pages, not bytes. Additionally, fixes the max limit sentinel value. -- [#1682](https://github.com/wasmerio/wasmer/pull/1682) Improve error reporting when making a memory with invalid settings. +### Added - [#1635](https://github.com/wasmerio/wasmer/pull/1635) Implement `wat2wasm` in the Wasm C API. - [#1636](https://github.com/wasmerio/wasmer/pull/1636) Implement `wasm_module_validate` in the Wasm C API. +- [#1657](https://github.com/wasmerio/wasmer/pull/1657) Implement `wasm_trap_t` and `wasm_frame_t` for Wasm C API; add examples in Rust and C of exiting early with a host function. + +### Fixed +- [#1690](https://github.com/wasmerio/wasmer/pull/1690) Fix `wasm_memorytype_limits` where `min` and `max` represents pages, not bytes. Additionally, fixes the max limit sentinel value. - [#1671](https://github.com/wasmerio/wasmer/pull/1671) Fix probestack firing inappropriately, and sometimes over/under allocating stack. - [#1660](https://github.com/wasmerio/wasmer/pull/1660) Fix issue preventing map-dir aliases starting with `/` from working properly. -- [#1657](https://github.com/wasmerio/wasmer/pull/1657) Implement `wasm_trap_t` and `wasm_frame_t` for Wasm C API; add examples in Rust and C of exiting early with a host function. + +### Changed +- [#1682](https://github.com/wasmerio/wasmer/pull/1682) Improve error reporting when making a memory with invalid settings. +- [#1691](https://github.com/wasmerio/wasmer/pull/1691) Bump minimum supported Rust version to 1.46.0 - [#1645](https://github.com/wasmerio/wasmer/pull/1645) Move the install script to https://github.com/wasmerio/wasmer-install ## 1.0.0-alpha3 - 2020-09-14 +### Fixed + - [#1620](https://github.com/wasmerio/wasmer/pull/1620) Fix bug causing the Wapm binary to not be packaged with the release - [#1619](https://github.com/wasmerio/wasmer/pull/1619) Improve error message in engine-native when C compiler is missing ## 1.0.0-alpha02.0 - 2020-09-11 +### Added + +- [#1566](https://github.com/wasmerio/wasmer/pull/1566) Add support for opening special Unix files to the WASI FS + +### Fixed + - [#1602](https://github.com/wasmerio/wasmer/pull/1602) Fix panic when calling host functions with negative numbers in certain situations - [#1590](https://github.com/wasmerio/wasmer/pull/1590) Fix soundness issue in API of vm::Global -- [#1566](https://github.com/wasmerio/wasmer/pull/1566) Add support for opening special Unix files to the WASI FS ## TODO: 1.0.0-alpha01.0 - Wasmer refactor lands -## TODO: 17... +## 0.17.1 - 2020-06-24 + +### Changed +- [#1439](https://github.com/wasmerio/wasmer/pull/1439) Move `wasmer-interface-types` into its own repository + +### Fixed - [#1554](https://github.com/wasmerio/wasmer/pull/1554) Update supported stable Rust version to 1.45.2. - [#1552](https://github.com/wasmerio/wasmer/pull/1552) Disable `sigint` handler by default. -## 0.17.1 - 2020-06-24 - -- [#1439](https://github.com/wasmerio/wasmer/pull/1439) Move `wasmer-interface-types` into its own repository - ## 0.17.0 - 2020-05-11 -- [#1401](https://github.com/wasmerio/wasmer/pull/1401) Make breaking change to `RuntimeError`: `RuntimeError` is now more explicit about its possible error values allowing for better insight into why a call into Wasm failed. -- [#1382](https://github.com/wasmerio/wasmer/pull/1382) Refactored test infranstructure (part 2) -- [#1380](https://github.com/wasmerio/wasmer/pull/1380) Refactored test infranstructure (part 1) -- [#1357](https://github.com/wasmerio/wasmer/pull/1357) Refactored bin commands into separate files +### Added - [#1331](https://github.com/wasmerio/wasmer/pull/1331) Implement the `record` type and instrutions for WIT - [#1345](https://github.com/wasmerio/wasmer/pull/1345) Adding ARM testing in Azure Pipelines -- [#1335](https://github.com/wasmerio/wasmer/pull/1335) Change mutability of `memory` to `const` in `wasmer_memory_data_length` in the C API - [#1329](https://github.com/wasmerio/wasmer/pull/1329) New numbers and strings instructions for WIT -- [#1332](https://github.com/wasmerio/wasmer/pull/1332) Add option to `CompilerConfig` to force compiler IR verification off even when `debug_assertions` are enabled. This can be used to make debug builds faster, which may be important if you're creating a library that wraps Wasmer and depend on the speed of debug builds. -- [#1320](https://github.com/wasmerio/wasmer/pull/1320) Change `custom_sections` field in `ModuleInfo` to be more standards compliant by allowing multiple custom sections with the same name. To get the old behavior with the new API, you can add `.last().unwrap()` to accesses. For example, `module_info.custom_sections["custom_section_name"].last().unwrap()`. +- [#1285](https://github.com/wasmerio/wasmer/pull/1285) Greatly improve errors in `wasmer-interface-types` +- [#1303](https://github.com/wasmerio/wasmer/pull/1303) NaN canonicalization for singlepass backend. - [#1313](https://github.com/wasmerio/wasmer/pull/1313) Add new high-level public API through `wasmer` crate. Includes many updates including: - Minor improvement: `imports!` macro now handles no trailing comma as well as a trailing comma in namespaces and between namespaces. - New methods on `Module`: `exports`, `imports`, and `custom_sections`. - New way to get exports from an instance with `let func_name: Func = instance.exports.get("func_name");`. - Improved `Table` APIs including `set` which now allows setting functions directly. TODO: update this more if `Table::get` gets made public in this PR - TODO: finish the list of changes here -- [#1303](https://github.com/wasmerio/wasmer/pull/1303) NaN canonicalization for singlepass backend. -- [#1292](https://github.com/wasmerio/wasmer/pull/1292) Experimental Support for Android (x86_64 and AArch64) - [#1305](https://github.com/wasmerio/wasmer/pull/1305) Handle panics from DynamicFunc. -- [#1301](https://github.com/wasmerio/wasmer/pull/1301) Update supported stable Rust version to 1.41.1. - [#1300](https://github.com/wasmerio/wasmer/pull/1300) Add support for multiple versions of WASI tests: wasitests now test all versions of WASI. -- [#1285](https://github.com/wasmerio/wasmer/pull/1285) Greatly improve errors in `wasmer-interface-types` +- [#1292](https://github.com/wasmerio/wasmer/pull/1292) Experimental Support for Android (x86_64 and AArch64) + +### Fixed - [#1283](https://github.com/wasmerio/wasmer/pull/1283) Workaround for floating point arguments and return values in `DynamicFunc`s. +### Changed +- [#1401](https://github.com/wasmerio/wasmer/pull/1401) Make breaking change to `RuntimeError`: `RuntimeError` is now more explicit about its possible error values allowing for better insight into why a call into Wasm failed. +- [#1382](https://github.com/wasmerio/wasmer/pull/1382) Refactored test infranstructure (part 2) +- [#1380](https://github.com/wasmerio/wasmer/pull/1380) Refactored test infranstructure (part 1) +- [#1357](https://github.com/wasmerio/wasmer/pull/1357) Refactored bin commands into separate files +- [#1335](https://github.com/wasmerio/wasmer/pull/1335) Change mutability of `memory` to `const` in `wasmer_memory_data_length` in the C API +- [#1332](https://github.com/wasmerio/wasmer/pull/1332) Add option to `CompilerConfig` to force compiler IR verification off even when `debug_assertions` are enabled. This can be used to make debug builds faster, which may be important if you're creating a library that wraps Wasmer and depend on the speed of debug builds. +- [#1320](https://github.com/wasmerio/wasmer/pull/1320) Change `custom_sections` field in `ModuleInfo` to be more standards compliant by allowing multiple custom sections with the same name. To get the old behavior with the new API, you can add `.last().unwrap()` to accesses. For example, `module_info.custom_sections["custom_section_name"].last().unwrap()`. +- [#1301](https://github.com/wasmerio/wasmer/pull/1301) Update supported stable Rust version to 1.41.1. + ## 0.16.2 - 2020-03-11 +### Fixed + - [#1294](https://github.com/wasmerio/wasmer/pull/1294) Fix bug related to system calls in WASI that rely on reading from WasmPtrs as arrays of length 0. `WasmPtr` will now succeed on length 0 arrays again. ## 0.16.1 - 2020-03-11 +### Fixed + - [#1291](https://github.com/wasmerio/wasmer/pull/1291) Fix installation packaging script to package the `wax` command. ## 0.16.0 - 2020-03-11 +### Added - [#1286](https://github.com/wasmerio/wasmer/pull/1286) Updated Windows Wasmer icons. Add wax - [#1284](https://github.com/wasmerio/wasmer/pull/1284) Implement string and memory instructions in `wasmer-interface-types` + +### Fixed - [#1272](https://github.com/wasmerio/wasmer/pull/1272) Fix off-by-one error bug when accessing memory with a `WasmPtr` that contains the last valid byte of memory. Also changes the behavior of `WasmPtr` with a length of 0 and `WasmPtr` where `std::mem::size_of::()` is 0 to always return `None` ## 0.15.0 - 2020-03-04 From a0b237c1f6ddb44853772021f043aef4495cdeba Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 12 Oct 2020 15:04:25 -0700 Subject: [PATCH 15/49] Add test using mapdir and dir, fix misc bugs --- lib/cli/src/commands/wasmer_create_exe_main.c | 10 +-- tests/integration/cli/src/link_code.rs | 4 + tests/integration/cli/src/util.rs | 7 +- tests/integration/cli/tests/compile.rs | 40 +++++---- tests/integration/cli/tests/create_exe.rs | 83 +++++++++++++++++-- 5 files changed, 116 insertions(+), 28 deletions(-) diff --git a/lib/cli/src/commands/wasmer_create_exe_main.c b/lib/cli/src/commands/wasmer_create_exe_main.c index 6b56c02fc..be7936731 100644 --- a/lib/cli/src/commands/wasmer_create_exe_main.c +++ b/lib/cli/src/commands/wasmer_create_exe_main.c @@ -44,20 +44,19 @@ void pass_mapdir_arg(wasi_config_t* wasi_config, char* mapdir) { fprintf(stderr, "Expected mapdir argument of the form alias:directory\n"); exit(-1); } - // TODO: triple check for off by one errors int dir_len = strlen(mapdir) - colon_location; - char* alias = (char*)malloc(colon_location); - char* dir = (char*)malloc(dir_len); + char* alias = (char*)malloc(colon_location + 1); + char* dir = (char*)malloc(dir_len + 1); int j = 0; for (j = 0; j < colon_location; ++j) { alias[j] = mapdir[j]; } alias[j] = 0; for (j = 0; j < dir_len; ++j) { - dir[j] = mapdir[j + colon_location]; + dir[j] = mapdir[j + colon_location + 1]; } dir[j] = 0; - + wasi_config_mapdir(wasi_config, alias, dir); free(alias); free(dir); @@ -107,7 +106,6 @@ void handle_arguments(wasi_config_t* wasi_config, int argc, char* argv[]) { #endif int main(int argc, char* argv[]) { - printf("Initializing...\n"); wasm_config_t* config = wasm_config_new(); wasm_config_set_engine(config, OBJECT_FILE); wasm_engine_t* engine = wasm_engine_new_with_config(config); diff --git a/tests/integration/cli/src/link_code.rs b/tests/integration/cli/src/link_code.rs index 72fb18e1b..d900f3e66 100644 --- a/tests/integration/cli/src/link_code.rs +++ b/tests/integration/cli/src/link_code.rs @@ -6,6 +6,8 @@ use std::process::Command; /// Data used to run a linking command for generated artifacts. #[derive(Debug)] pub struct LinkCode { + /// The directory to operate in. + pub current_dir: PathBuf, /// Path to the linker used to run the linking command. pub linker_path: PathBuf, /// String used as an optimization flag. @@ -25,6 +27,7 @@ impl Default for LinkCode { #[cfg(windows)] let linker = "clang"; Self { + current_dir: std::env::current_dir().unwrap(), linker_path: PathBuf::from(linker), optimization_flag: String::from("-O2"), object_paths: vec![], @@ -38,6 +41,7 @@ impl LinkCode { pub fn run(&self) -> anyhow::Result<()> { let mut command = Command::new(&self.linker_path); let command = command + .current_dir(&self.current_dir) .arg(&self.optimization_flag) .args( self.object_paths diff --git a/tests/integration/cli/src/util.rs b/tests/integration/cli/src/util.rs index 8cfcaae20..300067327 100644 --- a/tests/integration/cli/src/util.rs +++ b/tests/integration/cli/src/util.rs @@ -36,8 +36,13 @@ impl Engine { } } -pub fn run_code(executable_path: &Path, args: &[String]) -> anyhow::Result { +pub fn run_code( + operating_dir: &Path, + executable_path: &Path, + args: &[String], +) -> anyhow::Result { let output = Command::new(executable_path.canonicalize()?) + .current_dir(operating_dir) .args(args) .output()?; diff --git a/tests/integration/cli/tests/compile.rs b/tests/integration/cli/tests/compile.rs index fcbedc938..71fa6bd58 100644 --- a/tests/integration/cli/tests/compile.rs +++ b/tests/integration/cli/tests/compile.rs @@ -18,6 +18,8 @@ fn object_file_engine_test_wasm_path() -> String { /// 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. @@ -39,6 +41,7 @@ impl Default for WasmerCompile { #[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(object_file_engine_test_wasm_path()), wasm_object_path: PathBuf::from(wasm_obj_path), @@ -52,6 +55,7 @@ impl Default for WasmerCompile { 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()) @@ -76,13 +80,18 @@ impl WasmerCompile { } /// Compile the C code. -fn run_c_compile(path_to_c_src: &Path, output_name: &Path) -> anyhow::Result<()> { +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) @@ -106,18 +115,18 @@ fn run_c_compile(path_to_c_src: &Path, output_name: &Path) -> anyhow::Result<()> #[test] fn object_file_engine_works() -> anyhow::Result<()> { - let operating_dir = tempfile::tempdir()?; + let temp_dir = tempfile::tempdir()?; + let operating_dir: PathBuf = temp_dir.path().to_owned(); - std::env::set_current_dir(&operating_dir)?; - - let wasm_path = PathBuf::from(object_file_engine_test_wasm_path()); + let wasm_path = operating_dir.join(object_file_engine_test_wasm_path()); #[cfg(not(windows))] - let wasm_object_path = PathBuf::from("wasm.o"); + let wasm_object_path = operating_dir.join("wasm.o"); #[cfg(windows)] - let wasm_object_path = PathBuf::from("wasm.obj"); - let header_output_path = PathBuf::from("my_wasm.h"); + 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_path.clone(), wasm_object_path: wasm_object_path.clone(), header_output_path, @@ -128,12 +137,12 @@ fn object_file_engine_works() -> anyhow::Result<()> { .run() .context("Failed to compile wasm with Wasmer")?; - let c_src_file_name = Path::new("c_src.c"); + let c_src_file_name = operating_dir.join("c_src.c"); #[cfg(not(windows))] - let c_object_path = PathBuf::from("c_src.o"); + let c_object_path = operating_dir.join("c_src.o"); #[cfg(windows)] - let c_object_path = PathBuf::from("c_src.obj"); - let executable_path = PathBuf::from("a.out"); + 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 { @@ -144,8 +153,10 @@ fn object_file_engine_works() -> anyhow::Result<()> { .context("Failed to open C source code file")?; c_src_file.write_all(OBJECT_FILE_ENGINE_TEST_C_SOURCE)?; } - run_c_compile(&c_src_file_name, &c_object_path).context("Failed to compile C source code")?; + 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() @@ -153,7 +164,8 @@ fn object_file_engine_works() -> anyhow::Result<()> { .run() .context("Failed to link objects together")?; - let result = run_code(&executable_path, &[]).context("Failed to run generated executable")?; + let result = run_code(&operating_dir, &executable_path, &[]) + .context("Failed to run generated executable")?; let result_lines = result.lines().collect::>(); assert_eq!(result_lines, vec!["Initializing...", "\"Hello, World\""],); diff --git a/tests/integration/cli/tests/create_exe.rs b/tests/integration/cli/tests/create_exe.rs index 2af9e390f..ec2eb54ff 100644 --- a/tests/integration/cli/tests/create_exe.rs +++ b/tests/integration/cli/tests/create_exe.rs @@ -1,6 +1,8 @@ //! Tests of the `wasmer create-exe` command. use anyhow::{bail, Context}; +use std::fs; +use std::io::prelude::*; use std::path::PathBuf; use std::process::Command; use wasmer_integration_tests_cli::*; @@ -8,10 +10,14 @@ use wasmer_integration_tests_cli::*; fn create_exe_test_wasm_path() -> String { format!("{}/{}", ASSET_PATH, "qjs.wasm") } +const JS_TEST_SRC_CODE: &[u8] = + b"function greet(name) { return JSON.stringify('Hello, ' + name); }; print(greet('World'));\n"; /// Data used to run the `wasmer compile` command. #[derive(Debug)] struct WasmerCreateExe { + /// 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. @@ -29,6 +35,7 @@ impl Default for WasmerCreateExe { #[cfg(windows)] let native_executable_path = PathBuf::from("wasm.exe"); Self { + current_dir: std::env::current_dir().unwrap(), wasmer_path: get_wasmer_path(), wasm_path: PathBuf::from(create_exe_test_wasm_path()), native_executable_path, @@ -40,6 +47,7 @@ impl Default for WasmerCreateExe { impl WasmerCreateExe { fn run(&self) -> anyhow::Result<()> { let output = Command::new(&self.wasmer_path) + .current_dir(&self.current_dir) .arg("create-exe") .arg(&self.wasm_path.canonicalize()?) .arg(&self.compiler.to_flag()) @@ -64,17 +72,17 @@ impl WasmerCreateExe { #[test] fn create_exe_works() -> anyhow::Result<()> { - let operating_dir = tempfile::tempdir()?; + let temp_dir = tempfile::tempdir()?; + let operating_dir: PathBuf = temp_dir.path().to_owned(); - std::env::set_current_dir(&operating_dir)?; - - let wasm_path = PathBuf::from(create_exe_test_wasm_path()); + let wasm_path = operating_dir.join(create_exe_test_wasm_path()); #[cfg(not(windows))] - let executable_path = PathBuf::from("wasm.out"); + let executable_path = operating_dir.join("wasm.out"); #[cfg(windows)] - let executable_path = PathBuf::from("wasm.exe"); + let executable_path = operating_dir.join("wasm.exe"); WasmerCreateExe { + current_dir: operating_dir.clone(), wasm_path: wasm_path.clone(), native_executable_path: executable_path.clone(), compiler: Compiler::Cranelift, @@ -84,12 +92,73 @@ fn create_exe_works() -> anyhow::Result<()> { .context("Failed to create-exe wasm with Wasmer")?; let result = run_code( + &operating_dir, &executable_path, &["--eval".to_string(), "function greet(name) { return JSON.stringify('Hello, ' + name); }; print(greet('World'));".to_string()], ) .context("Failed to run generated executable")?; let result_lines = result.lines().collect::>(); - assert_eq!(result_lines, vec!["Initializing...", "\"Hello, World\""],); + assert_eq!(result_lines, vec!["\"Hello, World\""],); + + Ok(()) +} + +#[test] +fn create_exe_works_with_file() -> anyhow::Result<()> { + let temp_dir = tempfile::tempdir()?; + let operating_dir: PathBuf = temp_dir.path().to_owned(); + + let wasm_path = operating_dir.join(create_exe_test_wasm_path()); + #[cfg(not(windows))] + let executable_path = operating_dir.join("wasm.out"); + #[cfg(windows)] + let executable_path = operating_dir.join("wasm.exe"); + + WasmerCreateExe { + current_dir: operating_dir.clone(), + wasm_path: wasm_path.clone(), + native_executable_path: executable_path.clone(), + compiler: Compiler::Cranelift, + ..Default::default() + } + .run() + .context("Failed to create-exe wasm with Wasmer")?; + + { + let mut f = fs::OpenOptions::new() + .write(true) + .create_new(true) + .open(operating_dir.join("test.js"))?; + f.write_all(JS_TEST_SRC_CODE)?; + } + + // test with `--dir` + let result = run_code( + &operating_dir, + &executable_path, + &[ + "--dir=.".to_string(), + "--script".to_string(), + "test.js".to_string(), + ], + ) + .context("Failed to run generated executable")?; + let result_lines = result.lines().collect::>(); + assert_eq!(result_lines, vec!["\"Hello, World\""],); + + // test with `--mapdir` + let result = run_code( + &operating_dir, + &executable_path, + &[ + "--mapdir=abc:.".to_string(), + "--script".to_string(), + "abc/test.js".to_string(), + ], + ) + .context("Failed to run generated executable")?; + let result_lines = result.lines().collect::>(); + assert_eq!(result_lines, vec!["\"Hello, World\""],); Ok(()) } From 82aea12849a664006904cc15333e8f1d9d762898 Mon Sep 17 00:00:00 2001 From: Nick Lewycky Date: Mon, 12 Oct 2020 15:34:27 -0700 Subject: [PATCH 16/49] Export the VM functions when building on ARM. This is needed to be able to run engine-native built object files on aarch64 linux. --- .cargo/config.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.cargo/config.toml b/.cargo/config.toml index d703a1426..40da867bb 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -2,3 +2,8 @@ rustflags = [ "-C", "link-arg=-Wl,-E" ] + +[target.aarch64-unknown-linux-gnu] +rustflags = [ + "-C", "link-arg=-Wl,-E" +] From 331ff2b0d11b6f9e598fd2f23373e4adb03d32da Mon Sep 17 00:00:00 2001 From: Nick Lewycky Date: Mon, 12 Oct 2020 15:39:51 -0700 Subject: [PATCH 17/49] Update the commented-out code to run tests with engine-native. --- tests/compilers/utils.rs | 5 +++++ tests/compilers/wast.rs | 15 +++++---------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/compilers/utils.rs b/tests/compilers/utils.rs index 8bcbfb419..5470b23c6 100644 --- a/tests/compilers/utils.rs +++ b/tests/compilers/utils.rs @@ -3,6 +3,7 @@ use wasmer::{FunctionMiddlewareGenerator, Store}; use wasmer_compiler::CompilerConfig; use wasmer_engine::Engine; use wasmer_engine_jit::JIT; +// use wasmer_engine_native::Native; pub fn get_compiler(canonicalize_nans: bool) -> impl CompilerConfig { cfg_if::cfg_if! { @@ -35,6 +36,8 @@ pub fn get_compiler(canonicalize_nans: bool) -> impl CompilerConfig { pub fn get_engine() -> impl Engine { let compiler_config = get_compiler(false); JIT::new(&compiler_config).engine() + // let mut compiler_config = get_compiler(false); + // Native::new(&mut compiler_config).engine() } pub fn get_store() -> Store { @@ -49,9 +52,11 @@ pub fn get_store_with_middlewares Store { Store::new(&JIT::headless().engine()) + // Store::new(&Native::headless().engine()) } diff --git a/tests/compilers/wast.rs b/tests/compilers/wast.rs index 82a83dd59..8a43af147 100644 --- a/tests/compilers/wast.rs +++ b/tests/compilers/wast.rs @@ -6,7 +6,7 @@ use wasmer::{Features, Store}; #[cfg(feature = "jit")] use wasmer_engine_jit::JIT; // #[cfg(feature = "native")] -// use wasmer_engine_native::NativeEngine; +// use wasmer_engine_native::Native; use wasmer_wast::Wast; // The generated tests (from build.rs) look like: @@ -21,13 +21,6 @@ use wasmer_wast::Wast; // } include!(concat!(env!("OUT_DIR"), "/generated_spectests.rs")); -// This prefixer returns the hash of the module to prefix each of -// the functions in the shared object generated by the `NativeEngine`. -fn _native_prefixer(bytes: &[u8]) -> String { - let hash = blake3::hash(bytes); - format!("{}", hash.to_hex()) -} - pub fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> { println!( "Running wast `{}` with the {} compiler", @@ -47,8 +40,10 @@ pub fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> { features.multi_value(false); let compiler_config = get_compiler(try_nan_canonicalization); let store = Store::new(&JIT::new(&compiler_config).features(features).engine()); - // let mut native = NativeEngine::new(compiler_config, tunables); - // native.set_deterministic_prefixer(native_prefixer); + // let mut compiler_config = compiler_config; + // let native = Native::new(&mut compiler_config) + // .features(features) + // .engine(); // let store = Store::new(&native); let mut wast = Wast::new_with_spectest(store); if is_simd { From d4c92c3aaf65d1f7afe000a7f83184fbed70fe01 Mon Sep 17 00:00:00 2001 From: Nick Lewycky Date: Mon, 12 Oct 2020 15:45:34 -0700 Subject: [PATCH 18/49] Restore this function. --- tests/compilers/wast.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/compilers/wast.rs b/tests/compilers/wast.rs index 8a43af147..726d0590b 100644 --- a/tests/compilers/wast.rs +++ b/tests/compilers/wast.rs @@ -21,6 +21,13 @@ use wasmer_wast::Wast; // } include!(concat!(env!("OUT_DIR"), "/generated_spectests.rs")); +// This prefixer returns the hash of the module to prefix each of +// the functions in the shared object generated by the `NativeEngine`. +fn _native_prefixer(bytes: &[u8]) -> String { + let hash = blake3::hash(bytes); + format!("{}", hash.to_hex()) +} + pub fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> { println!( "Running wast `{}` with the {} compiler", From a36d6ede282f0726168391d70956a3425f3a38e7 Mon Sep 17 00:00:00 2001 From: Nick Lewycky Date: Mon, 12 Oct 2020 15:50:12 -0700 Subject: [PATCH 19/49] Generalize this for all CPUs. --- .cargo/config.toml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 40da867bb..5ae061b86 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,9 +1,4 @@ -[target.x86_64-unknown-linux-gnu] -rustflags = [ - "-C", "link-arg=-Wl,-E" -] - -[target.aarch64-unknown-linux-gnu] +[target.'cfg(target_os = "linux")'] rustflags = [ "-C", "link-arg=-Wl,-E" ] From 81fd4fc0426bebaec6a5a70d55e2b82f3fb6327a Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 12 Oct 2020 15:53:46 -0700 Subject: [PATCH 20/49] Clean create-exe code a bit --- Makefile | 1 + lib/cli/src/commands/create_exe.rs | 54 ++++++++++--------- lib/cli/src/commands/wasmer_create_exe_main.c | 2 +- tests/integration/cli/src/link_code.rs | 3 +- tests/integration/cli/tests/create_exe.rs | 2 - 5 files changed, 32 insertions(+), 30 deletions(-) diff --git a/Makefile b/Makefile index 8d07bf70c..dd39c5afc 100644 --- a/Makefile +++ b/Makefile @@ -166,6 +166,7 @@ package-capi: mkdir -p "package/include" mkdir -p "package/lib" cp lib/c-api/wasmer.h* package/include + cp lib/c-api/wasm.h* package/include cp lib/c-api/doc/index.md package/include/README.md ifeq ($(OS), Windows_NT) cp target/release/wasmer_c_api.dll package/lib diff --git a/lib/cli/src/commands/create_exe.rs b/lib/cli/src/commands/create_exe.rs index 3caa57944..d4893e947 100644 --- a/lib/cli/src/commands/create_exe.rs +++ b/lib/cli/src/commands/create_exe.rs @@ -11,9 +11,6 @@ use wasmer::*; const WASMER_MAIN_C_SOURCE: &[u8] = include_bytes!("wasmer_create_exe_main.c"); -// TODO: to get this working locally I had to add wasmer_wasm.h and wasm.h to `.wasmer/include` manually. -// this needs to be fixed before we can ship this. - #[derive(Debug, StructOpt)] /// The options for the `wasmer create-exe` subcommand pub struct CreateExe { @@ -73,8 +70,6 @@ impl CreateExe { let output_path = starting_cd.join(&self.output); env::set_current_dir(&working_dir)?; - // TODO: encapsulate compile code - #[cfg(not(windows))] let wasm_object_path = PathBuf::from("wasm.o"); #[cfg(windows)] @@ -82,7 +77,6 @@ impl CreateExe { let wasm_module_path = starting_cd.join(&self.path); - let header_file_path = Path::new("my_wasm.h"); let module = Module::from_file(&store, &wasm_module_path).context("failed to compile Wasm")?; let _ = module.serialize_to_file(&wasm_object_path)?; @@ -100,19 +94,19 @@ impl CreateExe { metadata_length, ); - let header_path = header_file_path.clone(); - // for C code - let mut header = std::fs::OpenOptions::new() - .create(true) - .truncate(true) - .write(true) - .open(&header_path)?; + 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; - header.write(header_file_src.as_bytes())?; - - // auto compilation - // // write C src to disk let c_src_path = Path::new("wasmer_main.c"); @@ -127,7 +121,6 @@ impl CreateExe { .write(true) .open(&c_src_path) .context("Failed to open C source code file")?; - // TODO: c_src_file.write_all(WASMER_MAIN_C_SOURCE)?; } run_c_compile(&c_src_path, &c_src_obj, self.target_triple.clone()) @@ -142,15 +135,24 @@ impl CreateExe { .run() .context("Failed to link objects together")?; - eprintln!( - "✔ Native executable compiled successfully to `{}`.", - self.output.display(), - ); - 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(header_file_src)?; + + Ok(()) +} + fn get_wasmer_dir() -> anyhow::Result { Ok(PathBuf::from( env::var("WASMER_DIR").context("Trying to read env var `WASMER_DIR`")?, @@ -168,6 +170,7 @@ fn get_libwasmer_path() -> anyhow::Result { 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)] @@ -253,8 +256,6 @@ impl Default for LinkCode { } impl LinkCode { - // TODO: `wasmer create-exe` needs a command line flag for extra libraries to link aganist - // or perhaps we just want to add a flag for passing things to the linker fn run(&self) -> anyhow::Result<()> { let mut command = Command::new(&self.linker_path); let command = command @@ -275,8 +276,11 @@ impl LinkCode { } else { command }; + // Add libraries required per platform. + // We need userenv, sockets (Ws2_32), and advapi32 to call a system call (for random numbers I think). #[cfg(windows)] let command = command.arg("-luserenv").arg("-lWs2_32").arg("-ladvapi32"); + // 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 diff --git a/lib/cli/src/commands/wasmer_create_exe_main.c b/lib/cli/src/commands/wasmer_create_exe_main.c index be7936731..11adb1656 100644 --- a/lib/cli/src/commands/wasmer_create_exe_main.c +++ b/lib/cli/src/commands/wasmer_create_exe_main.c @@ -111,7 +111,7 @@ int main(int argc, char* argv[]) { wasm_engine_t* engine = wasm_engine_new_with_config(config); wasm_store_t* store = wasm_store_new(engine); - wasm_module_t* module = wasmer_object_file_engine_new(store, "qjs.wasm"); + wasm_module_t* module = wasmer_object_file_engine_new(store, argv[0]); if (! module) { fprintf(stderr, "Failed to create module\n"); print_wasmer_error(); diff --git a/tests/integration/cli/src/link_code.rs b/tests/integration/cli/src/link_code.rs index d900f3e66..3dce23635 100644 --- a/tests/integration/cli/src/link_code.rs +++ b/tests/integration/cli/src/link_code.rs @@ -52,8 +52,7 @@ impl LinkCode { #[cfg(windows)] let command = command.arg("-luserenv").arg("-lWs2_32").arg("-ladvapi32"); #[cfg(not(windows))] - // TODO: remove `-lffi` before shipping - let command = command.arg("-ldl").arg("-lm").arg("-pthread").arg("-lffi"); + let command = command.arg("-ldl").arg("-lm").arg("-pthread"); let output = command.arg("-o").arg(&self.output_path).output()?; if !output.status.success() { diff --git a/tests/integration/cli/tests/create_exe.rs b/tests/integration/cli/tests/create_exe.rs index ec2eb54ff..b8302bccd 100644 --- a/tests/integration/cli/tests/create_exe.rs +++ b/tests/integration/cli/tests/create_exe.rs @@ -51,8 +51,6 @@ impl WasmerCreateExe { .arg("create-exe") .arg(&self.wasm_path.canonicalize()?) .arg(&self.compiler.to_flag()) - // TODO: remove before shipping - .arg("-lffi") .arg("-o") .arg(&self.native_executable_path) .output()?; From d51cff62bdb426317ac7b035f721d63e369d2af4 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 12 Oct 2020 15:59:27 -0700 Subject: [PATCH 21/49] Fix merge issue caused by GitHub UI --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f213bb2f6..106e2781c 100644 --- a/Makefile +++ b/Makefile @@ -174,7 +174,7 @@ package-capi: mkdir -p "package/include" mkdir -p "package/lib" cp lib/c-api/wasmer.h* package/include - cp lib/c-api/wasmer_wasm.h* package/include + cp lib/c-api/wasmer_wasm.h* package/include cp lib/c-api/wasm.h* package/include cp lib/c-api/doc/deprecated/index.md package/include/README.md ifeq ($(OS), Windows_NT) From e430bbaf4f1ad8ee54b14faa17b9775fc6e3907d Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 12 Oct 2020 16:17:34 -0700 Subject: [PATCH 22/49] More explicitly feature gate `wasmer create-exe` --- lib/cli/src/bin/wasmer.rs | 4 ++-- lib/cli/src/commands.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/cli/src/bin/wasmer.rs b/lib/cli/src/bin/wasmer.rs index 6d72964bf..7c7496413 100644 --- a/lib/cli/src/bin/wasmer.rs +++ b/lib/cli/src/bin/wasmer.rs @@ -1,5 +1,5 @@ use anyhow::Result; -#[cfg(feature = "object-file")] +#[cfg(all(feature = "object-file", feature = "compiler"))] use wasmer_cli::commands::CreateExe; #[cfg(feature = "wast")] use wasmer_cli::commands::Wast; @@ -60,7 +60,7 @@ impl WasmerCLIOptions { Self::Cache(cache) => cache.execute(), Self::Validate(validate) => validate.execute(), Self::Compile(compile) => compile.execute(), - #[cfg(feature = "object-file")] + #[cfg(all(feature = "object-file", feature = "compiler"))] Self::CreateExe(create_exe) => create_exe.execute(), Self::Config(config) => config.execute(), Self::Inspect(inspect) => inspect.execute(), diff --git a/lib/cli/src/commands.rs b/lib/cli/src/commands.rs index 4d07c37e8..7107150cc 100644 --- a/lib/cli/src/commands.rs +++ b/lib/cli/src/commands.rs @@ -2,7 +2,7 @@ mod cache; mod compile; mod config; -#[cfg(feature = "object-file")] +#[cfg(all(feature = "object-file", feature = "compiler"))] mod create_exe; mod inspect; mod run; @@ -11,7 +11,7 @@ mod validate; #[cfg(feature = "wast")] mod wast; -#[cfg(feature = "object-file")] +#[cfg(all(feature = "object-file", feature = "compiler"))] pub use create_exe::*; #[cfg(feature = "wast")] pub use wast::*; From fc11c1006339a63d65764a3a40af27cfcc84929a Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 12 Oct 2020 16:25:40 -0700 Subject: [PATCH 23/49] Add comment about why Windows is using clang++ --- lib/cli/src/commands/create_exe.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/cli/src/commands/create_exe.rs b/lib/cli/src/commands/create_exe.rs index d4893e947..62b9a42bd 100644 --- a/lib/cli/src/commands/create_exe.rs +++ b/lib/cli/src/commands/create_exe.rs @@ -187,6 +187,8 @@ fn run_c_compile( ) -> 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++"; From aeb17cda6a1dbfb5b571429a55bd22b0f34340a9 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 12 Oct 2020 16:30:13 -0700 Subject: [PATCH 24/49] Fix cfg logic in wasmer.rs again --- lib/cli/src/bin/wasmer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cli/src/bin/wasmer.rs b/lib/cli/src/bin/wasmer.rs index 7c7496413..509bbdede 100644 --- a/lib/cli/src/bin/wasmer.rs +++ b/lib/cli/src/bin/wasmer.rs @@ -29,7 +29,7 @@ enum WasmerCLIOptions { Compile(Compile), /// Compile a WebAssembly binary into a native executable - #[cfg(feature = "object-file")] + #[cfg(all(feature = "object-file", feature = "compiler"))] #[structopt(name = "create-exe")] CreateExe(CreateExe), From d737ad7d9f2bc11ba93b223603c5aade4b3dbace Mon Sep 17 00:00:00 2001 From: Nick Lewycky Date: Mon, 12 Oct 2020 16:46:47 -0700 Subject: [PATCH 25/49] Add the ability to run tests with either jit or native. Don't actually make `make test` run them with native though, it doesn't pass yet. --- Cargo.toml | 7 +++++++ Makefile | 24 +++++++++++++++++------- tests/compilers/utils.rs | 22 +++++++++++++++++++--- tests/compilers/wast.rs | 26 ++++++++++++++++---------- 4 files changed, 59 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dafadf68b..666d015e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -129,6 +129,13 @@ test-llvm = [ "llvm", ] +test-native = [ + "native", +] +test-jit = [ + "jit", +] + # Disable trap asserts in the WAST tests. This is useful for running the tests in a # context where signal handling is a problem, such as tarpaulin for code coverage. test-no-traps = ["wasmer-wast/test-no-traps"] diff --git a/Makefile b/Makefile index 99a38fd98..2155fdb11 100644 --- a/Makefile +++ b/Makefile @@ -94,16 +94,26 @@ build-capi-llvm: # Testing # ########### -test: $(foreach compiler,$(compilers),test-$(compiler)) test-packages test-examples test-deprecated +test: $(foreach compiler,$(compilers),test-$(compiler)-jit) test-packages test-examples test-deprecated -test-singlepass: - cargo test --release $(compiler_features) --features "test-singlepass" +# Singlepass and native engine don't work together, this rule does nothing. +test-singlepass-native: + @: -test-cranelift: - cargo test --release $(compiler_features) --features "test-cranelift" +test-singlepass-jit: + cargo test --release $(compiler_features) --features "test-singlepass test-jit" -test-llvm: - cargo test --release $(compiler_features) --features "test-llvm" +test-cranelift-native: + cargo test --release $(compiler_features) --features "test-cranelift test-native" + +test-cranelift-jit: + cargo test --release $(compiler_features) --features "test-cranelift test-jit" + +test-llvm-native: + cargo test --release $(compiler_features) --features "test-llvm test-native" + +test-llvm-jit: + cargo test --release $(compiler_features) --features "test-llvm test-jit" test-packages: cargo test -p wasmer --release diff --git a/tests/compilers/utils.rs b/tests/compilers/utils.rs index 5470b23c6..b08163fec 100644 --- a/tests/compilers/utils.rs +++ b/tests/compilers/utils.rs @@ -2,8 +2,10 @@ use std::sync::Arc; use wasmer::{FunctionMiddlewareGenerator, Store}; use wasmer_compiler::CompilerConfig; use wasmer_engine::Engine; +#[cfg(feature = "test-jit")] use wasmer_engine_jit::JIT; -// use wasmer_engine_native::Native; +#[cfg(feature = "test-native")] +use wasmer_engine_native::Native; pub fn get_compiler(canonicalize_nans: bool) -> impl CompilerConfig { cfg_if::cfg_if! { @@ -33,12 +35,18 @@ pub fn get_compiler(canonicalize_nans: bool) -> impl CompilerConfig { } } -pub fn get_engine() -> impl Engine { +#[cfg(feature = "test-jit")] + pub fn get_engine() -> impl Engine { let compiler_config = get_compiler(false); JIT::new(&compiler_config).engine() // let mut compiler_config = get_compiler(false); // Native::new(&mut compiler_config).engine() } +#[cfg(feature = "test-native")] + pub fn get_engine() -> impl Engine { + let mut compiler_config = get_compiler(false); + Native::new(&mut compiler_config).engine() +} pub fn get_store() -> Store { Store::new(&get_engine()) @@ -51,12 +59,20 @@ pub fn get_store_with_middlewares Store { Store::new(&JIT::headless().engine()) // Store::new(&Native::headless().engine()) } + +#[cfg(feature = "test-native")] +pub fn get_headless_store() -> Store { + Store::new(&Native::headless().engine()) +} diff --git a/tests/compilers/wast.rs b/tests/compilers/wast.rs index 726d0590b..f200209e8 100644 --- a/tests/compilers/wast.rs +++ b/tests/compilers/wast.rs @@ -3,10 +3,10 @@ use crate::utils::get_compiler; use std::path::Path; use wasmer::{Features, Store}; -#[cfg(feature = "jit")] +#[cfg(feature = "test-jit")] use wasmer_engine_jit::JIT; -// #[cfg(feature = "native")] -// use wasmer_engine_native::Native; + #[cfg(feature = "test-native")] +use wasmer_engine_native::Native; use wasmer_wast::Wast; // The generated tests (from build.rs) look like: @@ -28,6 +28,18 @@ fn _native_prefixer(bytes: &[u8]) -> String { format!("{}", hash.to_hex()) } +#[cfg(feature = "test-jit")] +fn get_store(features: Features, try_nan_canonicalization: bool) -> Store { + let compiler_config = get_compiler(try_nan_canonicalization); + Store::new(&JIT::new(&compiler_config).features(features).engine()) +} + +#[cfg(feature = "test-native")] +fn get_store(features: Features, try_nan_canonicalization: bool) -> Store { + let mut compiler_config = get_compiler(try_nan_canonicalization); + Store::new(&Native::new(&mut compiler_config).features(features).engine()) +} + pub fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> { println!( "Running wast `{}` with the {} compiler", @@ -45,13 +57,7 @@ pub fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> { } #[cfg(feature = "test-singlepass")] features.multi_value(false); - let compiler_config = get_compiler(try_nan_canonicalization); - let store = Store::new(&JIT::new(&compiler_config).features(features).engine()); - // let mut compiler_config = compiler_config; - // let native = Native::new(&mut compiler_config) - // .features(features) - // .engine(); - // let store = Store::new(&native); + let store = get_store(features, try_nan_canonicalization); let mut wast = Wast::new_with_spectest(store); if is_simd { // We allow this, so tests can be run properly for `simd_const` test. From 344d082171d98323ddd8ade011dcce770ecc30ff Mon Sep 17 00:00:00 2001 From: Nick Lewycky Date: Mon, 12 Oct 2020 16:53:01 -0700 Subject: [PATCH 26/49] cargo fmt --- tests/compilers/utils.rs | 4 ++-- tests/compilers/wast.rs | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/compilers/utils.rs b/tests/compilers/utils.rs index b08163fec..1c0ede605 100644 --- a/tests/compilers/utils.rs +++ b/tests/compilers/utils.rs @@ -36,14 +36,14 @@ pub fn get_compiler(canonicalize_nans: bool) -> impl CompilerConfig { } #[cfg(feature = "test-jit")] - pub fn get_engine() -> impl Engine { +pub fn get_engine() -> impl Engine { let compiler_config = get_compiler(false); JIT::new(&compiler_config).engine() // let mut compiler_config = get_compiler(false); // Native::new(&mut compiler_config).engine() } #[cfg(feature = "test-native")] - pub fn get_engine() -> impl Engine { +pub fn get_engine() -> impl Engine { let mut compiler_config = get_compiler(false); Native::new(&mut compiler_config).engine() } diff --git a/tests/compilers/wast.rs b/tests/compilers/wast.rs index f200209e8..1471a990a 100644 --- a/tests/compilers/wast.rs +++ b/tests/compilers/wast.rs @@ -5,7 +5,7 @@ use std::path::Path; use wasmer::{Features, Store}; #[cfg(feature = "test-jit")] use wasmer_engine_jit::JIT; - #[cfg(feature = "test-native")] +#[cfg(feature = "test-native")] use wasmer_engine_native::Native; use wasmer_wast::Wast; @@ -37,7 +37,11 @@ fn get_store(features: Features, try_nan_canonicalization: bool) -> Store { #[cfg(feature = "test-native")] fn get_store(features: Features, try_nan_canonicalization: bool) -> Store { let mut compiler_config = get_compiler(try_nan_canonicalization); - Store::new(&Native::new(&mut compiler_config).features(features).engine()) + Store::new( + &Native::new(&mut compiler_config) + .features(features) + .engine(), + ) } pub fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> { From 84c140ecd8d47f9cb360eb5095933649b8022b50 Mon Sep 17 00:00:00 2001 From: Nick Lewycky Date: Mon, 12 Oct 2020 16:56:58 -0700 Subject: [PATCH 27/49] Remove dead code. --- tests/compilers/utils.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/compilers/utils.rs b/tests/compilers/utils.rs index 1c0ede605..675240b32 100644 --- a/tests/compilers/utils.rs +++ b/tests/compilers/utils.rs @@ -39,8 +39,6 @@ pub fn get_compiler(canonicalize_nans: bool) -> impl CompilerConfig { pub fn get_engine() -> impl Engine { let compiler_config = get_compiler(false); JIT::new(&compiler_config).engine() - // let mut compiler_config = get_compiler(false); - // Native::new(&mut compiler_config).engine() } #[cfg(feature = "test-native")] pub fn get_engine() -> impl Engine { From 7cf1e124ceaa00c7832585ee8722db9f09dd25a1 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 12 Oct 2020 17:21:34 -0700 Subject: [PATCH 28/49] Attempt to fake install Wasmer for integration tests --- .github/workflows/main.yaml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 9318e1094..333df8c69 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -150,19 +150,23 @@ jobs: run: | make build-wapm if: needs.setup.outputs.DOING_RELEASE == '1' + - name: Package Wasmer + run: | + make package - name: Run integration tests (Windows) shell: cmd run: | call refreshenv + cd dist + wasmer-windows.exe + cd .. make test-integration if: matrix.run_integration_tests && matrix.os == 'windows-latest' - name: Run integration tests (Unix) - run: make test-integration - if: matrix.run_integration_tests && matrix.os != 'windows-latest' - - name: Package Wasmer run: | - make package - if: needs.setup.outputs.DOING_RELEASE == '1' + export WASMER_DIR=`pwd`/package + make test-integration + if: matrix.run_integration_tests && matrix.os != 'windows-latest' - name: Upload Artifacts uses: actions/upload-artifact@v2 if: needs.setup.outputs.DOING_RELEASE == '1' From f336ac13caf61bb8608d97035316687bd30b0f7a Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 12 Oct 2020 17:30:36 -0700 Subject: [PATCH 29/49] Lie to Windows about installation of Wasmer for tests too --- .github/workflows/main.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 333df8c69..8b2ce3165 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -157,9 +157,7 @@ jobs: shell: cmd run: | call refreshenv - cd dist - wasmer-windows.exe - cd .. + setx WASMER_DIR "%CD%/package" make test-integration if: matrix.run_integration_tests && matrix.os == 'windows-latest' - name: Run integration tests (Unix) From 30d34f1b5f2b52d8654a00858700a21c07024e69 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 12 Oct 2020 18:02:10 -0700 Subject: [PATCH 30/49] Package wasmer without wapm for integration tests --- .github/workflows/main.yaml | 4 ++++ Makefile | 3 +++ 2 files changed, 7 insertions(+) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 8b2ce3165..246d93682 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -150,9 +150,13 @@ jobs: run: | make build-wapm if: needs.setup.outputs.DOING_RELEASE == '1' + - name: Package Wasmer for integration tests + run: make package-without-wapm-for-integration-tests + if: needs.setup.outputs.DOING_RELEASE != '1' - name: Package Wasmer run: | make package + if: needs.setup.outputs.DOING_RELEASE == '1' - name: Run integration tests (Windows) shell: cmd run: | diff --git a/Makefile b/Makefile index 106e2781c..d79280cc3 100644 --- a/Makefile +++ b/Makefile @@ -214,6 +214,9 @@ else cp ./wasmer.tar.gz ./dist/$(shell ./scripts/binary-name.sh) endif +# command for simulating installing Wasmer without wapm. +package-without-wapm-for-integration-tests: package-wasmer package-capi + ################# # Miscellaneous # ################# From 2a448ba1173661e96fe3a7b9480bbc65179a9d88 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 13 Oct 2020 10:08:56 +0200 Subject: [PATCH 31/49] doc(changelog) Add #1709. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41c7a1d46..6041ead00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## **[Unreleased]** +- [#1709](https://github.com/wasmerio/wasmer/pull/1709) Implement `wasm_module_name` and `wasm_module_set_name` in the Wasm(er) C API. - [#1700](https://github.com/wasmerio/wasmer/pull/1700) Implement `wasm_externtype_copy` in the Wasm C API. ## 1.0.0-alpha4 - 2020-10-08 From 373b7a48154bce6f265907730a41fb11ba03df5f Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 13 Oct 2020 10:10:27 +0200 Subject: [PATCH 32/49] doc(changelog) Fix merge. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0bedf03e..edba3e674 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,9 @@ ## **[Unreleased]** -- [#1709](https://github.com/wasmerio/wasmer/pull/1709) Implement `wasm_module_name` and `wasm_module_set_name` in the Wasm(er) C API. ### Added +- [#1709](https://github.com/wasmerio/wasmer/pull/1709) Implement `wasm_module_name` and `wasm_module_set_name` in the Wasm(er) C API. - [#1700](https://github.com/wasmerio/wasmer/pull/1700) Implement `wasm_externtype_copy` in the Wasm C API. ## 1.0.0-alpha4 - 2020-10-08 From 954e5e849518204aa814eecd555ab4935b9ecf50 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 13 Oct 2020 10:26:32 +0200 Subject: [PATCH 33/49] fix(c-api) Register errors from `wasm_module_serialize`. --- lib/c-api/src/wasm_c_api/module.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/c-api/src/wasm_c_api/module.rs b/lib/c-api/src/wasm_c_api/module.rs index 6e334097b..dca2d6e3f 100644 --- a/lib/c-api/src/wasm_c_api/module.rs +++ b/lib/c-api/src/wasm_c_api/module.rs @@ -108,7 +108,10 @@ pub unsafe extern "C" fn wasm_module_serialize( ) { let byte_vec = match module.inner.serialize() { Ok(byte_vec) => byte_vec, - Err(_) => return, + Err(err) => { + crate::error::update_last_error(err); + return; + } }; *out_ptr = byte_vec.into(); } From 0dc74bf922a74a77172db027cb51a137805af182 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 13 Oct 2020 10:29:52 +0200 Subject: [PATCH 34/49] doc(changelog) Add #1715. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74d19d04d..ce2676920 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Added +- [#1715](https://github.com/wasmerio/wasmer/pull/1715) Register errors from `wasm_module_serialize` in the Wasm C API. - [#1700](https://github.com/wasmerio/wasmer/pull/1700) Implement `wasm_externtype_copy` in the Wasm C API. ## 1.0.0-alpha4 - 2020-10-08 From 7cc4d5adbe07a712bf57a4fef5a36e976f85e809 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 13 Oct 2020 10:49:38 +0200 Subject: [PATCH 35/49] fix(c-api) Simplify `NonNull` by `&T`. --- lib/c-api/src/wasm_c_api/types/value.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/c-api/src/wasm_c_api/types/value.rs b/lib/c-api/src/wasm_c_api/types/value.rs index 023539c36..dbb4b3a4d 100644 --- a/lib/c-api/src/wasm_c_api/types/value.rs +++ b/lib/c-api/src/wasm_c_api/types/value.rs @@ -1,6 +1,5 @@ use super::super::value::wasm_valkind_t; use std::convert::TryInto; -use std::ptr::NonNull; use wasmer::ValType; #[allow(non_camel_case_types)] @@ -84,11 +83,8 @@ pub extern "C" fn wasm_valtype_new(kind: wasm_valkind_t) -> Option>) {} #[no_mangle] -pub unsafe extern "C" fn wasm_valtype_kind( - valtype: Option>, -) -> wasm_valkind_t { +pub unsafe extern "C" fn wasm_valtype_kind(valtype: Option<&wasm_valtype_t>) -> wasm_valkind_t { valtype .expect("`wasm_valtype_kind: argument is a null pointer") - .as_ref() .valkind as wasm_valkind_t } From 58b05a9881790ce30890d00c8e96cce1389cfd29 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Tue, 13 Oct 2020 11:11:45 -0700 Subject: [PATCH 36/49] Fix env var setting on Windows --- .github/workflows/main.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 246d93682..d74303e3f 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -161,7 +161,7 @@ jobs: shell: cmd run: | call refreshenv - setx WASMER_DIR "%CD%/package" + set WASMER_DIR=%CD%\package make test-integration if: matrix.run_integration_tests && matrix.os == 'windows-latest' - name: Run integration tests (Unix) From e3ec9f34c12f68e768ebb234a50ee76421772de3 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Tue, 13 Oct 2020 14:21:17 -0700 Subject: [PATCH 37/49] Prevent panic when min > static bound and max is less than it --- CHANGELOG.md | 1 + lib/api/src/tunables.rs | 2 - lib/c-api/tests/wasm_c_api/test-memory.c | 60 ++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index edba3e674..df71323a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Added +- [#1718](https://github.com/wasmerio/wasmer/pull/1718) Fix panic in the API in some situations when the memory's min bound was greater than the memory's max bound. - [#1709](https://github.com/wasmerio/wasmer/pull/1709) Implement `wasm_module_name` and `wasm_module_set_name` in the Wasm(er) C API. - [#1700](https://github.com/wasmerio/wasmer/pull/1700) Implement `wasm_externtype_copy` in the Wasm C API. diff --git a/lib/api/src/tunables.rs b/lib/api/src/tunables.rs index 77c4c742c..2983d8042 100644 --- a/lib/api/src/tunables.rs +++ b/lib/api/src/tunables.rs @@ -1,5 +1,4 @@ use crate::{MemoryType, Pages, TableType}; -use more_asserts::assert_ge; use std::cmp::min; use std::sync::Arc; use target_lexicon::{OperatingSystem, PointerWidth}; @@ -67,7 +66,6 @@ impl BaseTunables for Tunables { // If the module doesn't declare an explicit maximum treat it as 4GiB. let maximum = memory.maximum.unwrap_or_else(Pages::max_value); if maximum <= self.static_memory_bound { - assert_ge!(self.static_memory_bound, memory.minimum); MemoryStyle::Static { bound: self.static_memory_bound, offset_guard_size: self.static_memory_offset_guard_size, diff --git a/lib/c-api/tests/wasm_c_api/test-memory.c b/lib/c-api/tests/wasm_c_api/test-memory.c index 3d4c8abe7..079768df8 100644 --- a/lib/c-api/tests/wasm_c_api/test-memory.c +++ b/lib/c-api/tests/wasm_c_api/test-memory.c @@ -62,6 +62,66 @@ int main(int argc, const char *argv[]) { wasm_memorytype_delete(memtype3); wasm_memory_delete(memory3); + // ===================== + wasm_limits_t limits4 = { + .min = 0x7FFFFFFF, + .max = 0x7FFFFFFF, + }; + own wasm_memorytype_t* memtype4 = wasm_memorytype_new(&limits4); + own wasm_memory_t* memory4 = wasm_memory_new(store, memtype4); + assert(memory4 == NULL); + error = get_wasmer_error(); + printf("Found error string: %s\n", error); + assert(0 == strcmp("The minimum requested (2147483647 pages) memory is greater than the maximum allowed memory (65536 pages)", error)); + free(error); + + wasm_memorytype_delete(memtype4); + + // ===================== + wasm_limits_t limits5 = { + .min = 0x7FFFFFFF, + .max = 0x0FFFFFFF, + }; + own wasm_memorytype_t* memtype5 = wasm_memorytype_new(&limits5); + own wasm_memory_t* memory5 = wasm_memory_new(store, memtype5); + assert(memory5 == NULL); + error = get_wasmer_error(); + printf("Found error string: %s\n", error); + assert(0 == strcmp("The minimum requested (2147483647 pages) memory is greater than the maximum allowed memory (65536 pages)", error)); + free(error); + + wasm_memorytype_delete(memtype5); + + // ===================== + wasm_limits_t limits6 = { + .min = 15, + .max = 10, + }; + own wasm_memorytype_t* memtype6 = wasm_memorytype_new(&limits6); + own wasm_memory_t* memory6 = wasm_memory_new(store, memtype6); + assert(memory6 == NULL); + error = get_wasmer_error(); + printf("Found error string: %s\n", error); + assert(0 == strcmp("The memory is invalid because the maximum (10 pages) is less than the minimum (15 pages)", error)); + free(error); + + wasm_memorytype_delete(memtype6); + + // ===================== + wasm_limits_t limits7 = { + .min = 0x7FFFFFFF, + .max = 10, + }; + own wasm_memorytype_t* memtype7 = wasm_memorytype_new(&limits7); + own wasm_memory_t* memory7 = wasm_memory_new(store, memtype7); + assert(memory7 == NULL); + error = get_wasmer_error(); + printf("Found error string: %s\n", error); + assert(0 == strcmp("The minimum requested (2147483647 pages) memory is greater than the maximum allowed memory (65536 pages)", error)); + free(error); + + wasm_memorytype_delete(memtype7); + printf("Shutting down...\n"); wasm_store_delete(store); wasm_engine_delete(engine); From 3a59e8a59d4c537856cfcbe128dd9b3be4a31f01 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Tue, 13 Oct 2020 14:45:19 -0700 Subject: [PATCH 38/49] Fix mistake in name of C API lib on Windows --- lib/cli/src/commands/create_exe.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cli/src/commands/create_exe.rs b/lib/cli/src/commands/create_exe.rs index 62b9a42bd..41b46d615 100644 --- a/lib/cli/src/commands/create_exe.rs +++ b/lib/cli/src/commands/create_exe.rs @@ -174,7 +174,7 @@ fn get_libwasmer_path() -> anyhow::Result { #[cfg(not(windows))] path.push("libwasmer.a"); #[cfg(windows)] - path.push("libwasmer.lib"); + path.push("wasmer_c_api.lib"); Ok(path) } From c97c70558d0dc59a8e97ad3f2fdb46635172a41d Mon Sep 17 00:00:00 2001 From: Syrus Date: Tue, 13 Oct 2020 18:27:29 -0700 Subject: [PATCH 39/49] Fixed broken intra docs attrs --- lib/api/src/lib.rs | 2 +- lib/vm/src/vmoffsets.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/api/src/lib.rs b/lib/api/src/lib.rs index 6725141ae..2a1f42c24 100644 --- a/lib/api/src/lib.rs +++ b/lib/api/src/lib.rs @@ -3,7 +3,7 @@ missing_docs, trivial_numeric_casts, unused_extern_crates, - intra_doc_link_resolution_failure + broken_intra_doc_links )] #![warn(unused_import_braces)] #![cfg_attr( diff --git a/lib/vm/src/vmoffsets.rs b/lib/vm/src/vmoffsets.rs index 1c0640843..b18408165 100644 --- a/lib/vm/src/vmoffsets.rs +++ b/lib/vm/src/vmoffsets.rs @@ -4,7 +4,7 @@ //! Offsets and sizes of various structs in wasmer-vm's vmcontext //! module. -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] use crate::module::ModuleInfo; use crate::VMBuiltinFunctionIndex; From 38ebdb94ef3ef2a5156ae2d18851335425dac55c Mon Sep 17 00:00:00 2001 From: Syrus Date: Tue, 13 Oct 2020 18:33:51 -0700 Subject: [PATCH 40/49] added create-exe as possible cli option --- lib/cli/src/bin/wasmer.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/cli/src/bin/wasmer.rs b/lib/cli/src/bin/wasmer.rs index 509bbdede..e0e51ed2d 100644 --- a/lib/cli/src/bin/wasmer.rs +++ b/lib/cli/src/bin/wasmer.rs @@ -82,9 +82,8 @@ fn main() { let args = std::env::args().collect::>(); let command = args.get(1); let options = match command.unwrap_or(&"".to_string()).as_ref() { - "run" | "cache" | "validate" | "compile" | "config" | "self-update" | "inspect" => { - WasmerCLIOptions::from_args() - } + "cache" | "compile" | "config" | "create-exe" | "help" | "inspect" | "run" + | "self-update" | "validate" | "wast" => WasmerCLIOptions::from_args(), _ => { WasmerCLIOptions::from_iter_safe(args.iter()).unwrap_or_else(|e| { match e.kind { From 773ab17a851ee755d94280d585774f48ddc3b3e2 Mon Sep 17 00:00:00 2001 From: Nick Lewycky Date: Wed, 14 Oct 2020 01:01:49 -0700 Subject: [PATCH 41/49] Add support for "on native" and "on jit" to the ignores.txt. Extend test-no-traps feature to cover tests/compiler/traps.rs and enable it when testing with the native engine. Turn on testing native engine as part of make test. --- Cargo.toml | 2 ++ Makefile | 6 +++--- tests/compilers/traps.rs | 7 +++++++ tests/ignores.txt | 4 ++++ tests/lib/test-generator/Cargo.toml | 4 ++++ tests/lib/test-generator/src/lib.rs | 13 +++++++++++-- 6 files changed, 31 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 666d015e4..e200eceed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -131,9 +131,11 @@ test-llvm = [ test-native = [ "native", + "test-generator/test-native", ] test-jit = [ "jit", + "test-generator/test-jit", ] # Disable trap asserts in the WAST tests. This is useful for running the tests in a diff --git a/Makefile b/Makefile index 7b7f944c5..2e7d26408 100644 --- a/Makefile +++ b/Makefile @@ -94,7 +94,7 @@ build-capi-llvm: # Testing # ########### -test: $(foreach compiler,$(compilers),test-$(compiler)-jit) test-packages test-examples test-deprecated +test: $(foreach compiler,$(compilers),test-$(compiler)-jit test-$(compiler)-native) test-packages test-examples test-deprecated # Singlepass and native engine don't work together, this rule does nothing. test-singlepass-native: @@ -104,13 +104,13 @@ test-singlepass-jit: cargo test --release $(compiler_features) --features "test-singlepass test-jit" test-cranelift-native: - cargo test --release $(compiler_features) --features "test-cranelift test-native" + cargo test --release $(compiler_features) --features "test-cranelift test-native test-no-traps" test-cranelift-jit: cargo test --release $(compiler_features) --features "test-cranelift test-jit" test-llvm-native: - cargo test --release $(compiler_features) --features "test-llvm test-native" + cargo test --release $(compiler_features) --features "test-llvm test-native test-no-traps" test-llvm-jit: cargo test --release $(compiler_features) --features "test-llvm test-jit" diff --git a/tests/compilers/traps.rs b/tests/compilers/traps.rs index cb00ffd22..78079f271 100644 --- a/tests/compilers/traps.rs +++ b/tests/compilers/traps.rs @@ -37,6 +37,7 @@ fn test_trap_return() -> Result<()> { Ok(()) } +#[cfg(not(feature = "test-no-traps"))] #[test] #[cfg_attr(feature = "test-singlepass", ignore)] fn test_trap_trace() -> Result<()> { @@ -117,6 +118,7 @@ fn test_trap_trace_cb() -> Result<()> { Ok(()) } +#[cfg(not(feature = "test-no-traps"))] #[test] #[cfg_attr(feature = "test-singlepass", ignore)] fn test_trap_stack_overflow() -> Result<()> { @@ -148,6 +150,7 @@ fn test_trap_stack_overflow() -> Result<()> { Ok(()) } +#[cfg(not(feature = "test-no-traps"))] #[test] #[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)] fn trap_display_pretty() -> Result<()> { @@ -181,6 +184,7 @@ RuntimeError: unreachable Ok(()) } +#[cfg(not(feature = "test-no-traps"))] #[test] #[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)] fn trap_display_multi_module() -> Result<()> { @@ -386,6 +390,7 @@ fn mismatched_arguments() -> Result<()> { Ok(()) } +#[cfg(not(feature = "test-no-traps"))] #[test] #[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)] fn call_signature_mismatch() -> Result<()> { @@ -417,6 +422,7 @@ RuntimeError: indirect call type mismatch Ok(()) } +#[cfg(not(feature = "test-no-traps"))] #[test] #[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)] fn start_trap_pretty() -> Result<()> { @@ -449,6 +455,7 @@ RuntimeError: unreachable Ok(()) } +#[cfg(not(feature = "test-no-traps"))] #[test] fn present_after_module_drop() -> Result<()> { let store = get_store(); diff --git a/tests/ignores.txt b/tests/ignores.txt index 38be894e1..cdc45ddad 100644 --- a/tests/ignores.txt +++ b/tests/ignores.txt @@ -22,6 +22,10 @@ singlepass on windows # Singlepass is not yet supported on Windows cranelift::spec::skip_stack_guard_page on darwin llvm::spec::skip_stack_guard_page on darwin +# TODO: traps in native engine +cranelift::spec::linking on native +llvm::spec::linking on native + # Frontends ## WASI diff --git a/tests/lib/test-generator/Cargo.toml b/tests/lib/test-generator/Cargo.toml index d9e3b4bea..6dfc8fa7d 100644 --- a/tests/lib/test-generator/Cargo.toml +++ b/tests/lib/test-generator/Cargo.toml @@ -7,3 +7,7 @@ publish = false [dependencies] anyhow = "1.0" target-lexicon = "0.10" + +[features] +test-native = [] +test-jit = [] diff --git a/tests/lib/test-generator/src/lib.rs b/tests/lib/test-generator/src/lib.rs index 0396fe8b9..f17ff21cd 100644 --- a/tests/lib/test-generator/src/lib.rs +++ b/tests/lib/test-generator/src/lib.rs @@ -47,6 +47,13 @@ pub fn build_ignores_from_textfile(path: PathBuf) -> anyhow::Result { let file = File::open(path)?; let reader = BufReader::new(file); let host = Triple::host().to_string(); + let engine = if cfg!(feature = "test-native") { + Some("native") + } else if cfg!(feature = "test-jit") { + Some("jit") + } else { + None + }; for line in reader.lines() { let line = line.unwrap(); // If the line has a `#` we discard all the content that comes after @@ -75,8 +82,10 @@ pub fn build_ignores_from_textfile(path: PathBuf) -> anyhow::Result { } // We skip the ignore if doesn't apply to the current - // host target - if target.map(|t| !host.contains(&t)).unwrap_or(false) { + // host target or engine + if target.clone().map(|t| !host.contains(&t)).unwrap_or(false) + && target.clone() != engine.map(str::to_string) + { continue; } From c24cb801053fba083e724af14f34388ceb416444 Mon Sep 17 00:00:00 2001 From: Nick Lewycky Date: Wed, 14 Oct 2020 12:40:05 -0700 Subject: [PATCH 42/49] Remove "test-no-traps" cfg! feature from test-generator, use a runtime bool instead. Rename "test-no-traps" feature to "coverage". Update .tarpaulin.toml to set coverage and also set test-jit (to fix breakage introduced in PR 1712). --- .tarpaulin.toml | 8 ++++---- Cargo.toml | 6 +++--- tests/compilers/wast.rs | 8 ++++++-- tests/lib/wast/Cargo.toml | 1 - tests/lib/wast/src/wast.rs | 28 ++++++++++++++++++---------- 5 files changed, 31 insertions(+), 20 deletions(-) diff --git a/.tarpaulin.toml b/.tarpaulin.toml index 78b59bfb0..08fe94c34 100644 --- a/.tarpaulin.toml +++ b/.tarpaulin.toml @@ -1,17 +1,17 @@ [cranelift_coverage] -features = "cranelift,singlepass,llvm,test-no-traps,test-cranelift" +features = "cranelift,singlepass,llvm,coverage,test-cranelift,test-jit" examples = ["early-exit", "engine-jit", "engine-native", "engine-headless", "cross-compilation", "compiler-cranelift", "exported-function", "wasi"] release = true [llvm_coverage] -features = "cranelift,singlepass,llvm,test-no-traps,test-llvm" +features = "cranelift,singlepass,llvm,coverage,test-llvm,test-jit" examples = ["compiler-llvm"] release = true [singlepass_coverage] -features = "cranelift,singlepass,llvm,test-no-traps,test-singlepass" +features = "cranelift,singlepass,llvm,coverage,test-singlepass,test-jit" examples = ["compiler-singlepass"] release = true [report] -out = ["Xml"] \ No newline at end of file +out = ["Xml"] diff --git a/Cargo.toml b/Cargo.toml index 666d015e4..4afe13373 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -136,9 +136,9 @@ test-jit = [ "jit", ] -# Disable trap asserts in the WAST tests. This is useful for running the tests in a -# context where signal handling is a problem, such as tarpaulin for code coverage. -test-no-traps = ["wasmer-wast/test-no-traps"] +# Specifies that we're running in coverage testing mode. This disables tests +# that raise signals because that interferes with tarpaulin. +coverage = [] [[bench]] name = "static_and_dynamic_functions" diff --git a/tests/compilers/wast.rs b/tests/compilers/wast.rs index 1471a990a..40e4d58c9 100644 --- a/tests/compilers/wast.rs +++ b/tests/compilers/wast.rs @@ -59,10 +59,14 @@ pub fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> { if is_simd { features.simd(true); } - #[cfg(feature = "test-singlepass")] - features.multi_value(false); + if cfg!(feature = "test-singlepass") { + features.multi_value(false); + } let store = get_store(features, try_nan_canonicalization); let mut wast = Wast::new_with_spectest(store); + if cfg!(feature = "coverage") { + wast.disable_assert_and_exhaustion(); + } if is_simd { // We allow this, so tests can be run properly for `simd_const` test. wast.allow_instantiation_failures(&[ diff --git a/tests/lib/wast/Cargo.toml b/tests/lib/wast/Cargo.toml index de453bb9e..b9c26627f 100644 --- a/tests/lib/wast/Cargo.toml +++ b/tests/lib/wast/Cargo.toml @@ -23,7 +23,6 @@ typetag = "0.1" [features] default = ["wat"] wat = ["wasmer/wat"] -test-no-traps = [] [badges] maintenance = { status = "actively-developed" } diff --git a/tests/lib/wast/src/wast.rs b/tests/lib/wast/src/wast.rs index a53f4ce66..2c6a07f74 100644 --- a/tests/lib/wast/src/wast.rs +++ b/tests/lib/wast/src/wast.rs @@ -24,6 +24,9 @@ pub struct Wast { store: Store, /// A flag indicating if Wast tests should stop as soon as one test fails. pub fail_fast: bool, + /// A flag indicating that assert_trap and assert_exhaustion should be skipped. + /// See https://github.com/wasmerio/wasmer/issues/1550 for more info + disable_assert_trap_exhaustion: bool, } impl Wast { @@ -37,10 +40,11 @@ impl Wast { current_is_allowed_failure: false, instances: HashMap::new(), fail_fast: true, + disable_assert_trap_exhaustion: false, } } - /// A list of instantiation failures to allow + /// A list of instantiation failures to allow. pub fn allow_instantiation_failures(&mut self, failures: &[&str]) { for &failure_str in failures.iter() { self.allowed_instantiation_failures @@ -48,6 +52,11 @@ impl Wast { } } + /// Do not run any code in assert_trap or assert_exhaustion. + pub fn disable_assert_and_exhaustion(&mut self) { + self.disable_assert_trap_exhaustion = true; + } + /// Construct a new instance of `Wast` with the spectests imports. pub fn new_with_spectest(store: Store) -> Self { let import_object = spectest_importobject(&store); @@ -155,27 +164,26 @@ impl Wast { let result = self.perform_execute(exec); self.assert_return(result, &results)?; } - #[cfg(not(feature = "test-no-traps"))] AssertTrap { span: _, exec, message, } => { - let result = self.perform_execute(exec); - self.assert_trap(result, message)?; + if !self.disable_assert_trap_exhaustion { + let result = self.perform_execute(exec); + self.assert_trap(result, message)?; + } } - #[cfg(not(feature = "test-no-traps"))] AssertExhaustion { span: _, call, message, } => { - let result = self.perform_invoke(call); - self.assert_trap(result, message)?; + if !self.disable_assert_trap_exhaustion { + let result = self.perform_invoke(call); + self.assert_trap(result, message)?; + } } - // See https://github.com/wasmerio/wasmer/issues/1550 for more info - #[cfg(feature = "test-no-traps")] - AssertTrap { .. } | AssertExhaustion { .. } => {} AssertInvalid { span: _, mut module, From 1f08f66ba6e8815b92a53fa065a166f82c865c1f Mon Sep 17 00:00:00 2001 From: nlewycky Date: Wed, 14 Oct 2020 12:58:03 -0700 Subject: [PATCH 43/49] Update tests/compilers/traps.rs Ignore the test instead of skipping it. Co-authored-by: Syrus Akbary --- tests/compilers/traps.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/compilers/traps.rs b/tests/compilers/traps.rs index 78079f271..4e8db9a14 100644 --- a/tests/compilers/traps.rs +++ b/tests/compilers/traps.rs @@ -39,7 +39,7 @@ fn test_trap_return() -> Result<()> { #[cfg(not(feature = "test-no-traps"))] #[test] -#[cfg_attr(feature = "test-singlepass", ignore)] +#[cfg_attr(any(feature = "test-singlepass", feature = "test-no-traps"), ignore)] fn test_trap_trace() -> Result<()> { let store = get_store(); let wat = r#" From e6cd0ab8d47d8011de3bef2a64b8ca9b310d0ed6 Mon Sep 17 00:00:00 2001 From: Nick Lewycky Date: Wed, 14 Oct 2020 13:39:32 -0700 Subject: [PATCH 44/49] Generalize the one-off code for matching of mismatched assert_trap messages. Use this to match all the mismatching trap messages in engine-native testing. --- Makefile | 4 ++-- tests/compilers/traps.rs | 48 +++++++++++++++++++++++++++----------- tests/compilers/wast.rs | 14 +++++++++++ tests/lib/wast/src/wast.rs | 25 ++++++++++++++------ 4 files changed, 69 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index 2e7d26408..48c72eb17 100644 --- a/Makefile +++ b/Makefile @@ -104,13 +104,13 @@ test-singlepass-jit: cargo test --release $(compiler_features) --features "test-singlepass test-jit" test-cranelift-native: - cargo test --release $(compiler_features) --features "test-cranelift test-native test-no-traps" + cargo test --release $(compiler_features) --features "test-cranelift test-native" test-cranelift-jit: cargo test --release $(compiler_features) --features "test-cranelift test-jit" test-llvm-native: - cargo test --release $(compiler_features) --features "test-llvm test-native test-no-traps" + cargo test --release $(compiler_features) --features "test-llvm test-native" test-llvm-jit: cargo test --release $(compiler_features) --features "test-llvm test-jit" diff --git a/tests/compilers/traps.rs b/tests/compilers/traps.rs index 4e8db9a14..7922511a0 100644 --- a/tests/compilers/traps.rs +++ b/tests/compilers/traps.rs @@ -37,9 +37,8 @@ fn test_trap_return() -> Result<()> { Ok(()) } -#[cfg(not(feature = "test-no-traps"))] #[test] -#[cfg_attr(any(feature = "test-singlepass", feature = "test-no-traps"), ignore)] +#[cfg_attr(any(feature = "test-singlepass", feature = "test-native"), ignore)] fn test_trap_trace() -> Result<()> { let store = get_store(); let wat = r#" @@ -118,9 +117,8 @@ fn test_trap_trace_cb() -> Result<()> { Ok(()) } -#[cfg(not(feature = "test-no-traps"))] #[test] -#[cfg_attr(feature = "test-singlepass", ignore)] +#[cfg_attr(any(feature = "test-singlepass", feature = "test-native"), ignore)] fn test_trap_stack_overflow() -> Result<()> { let store = get_store(); let wat = r#" @@ -150,9 +148,15 @@ fn test_trap_stack_overflow() -> Result<()> { Ok(()) } -#[cfg(not(feature = "test-no-traps"))] #[test] -#[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)] +#[cfg_attr( + any( + feature = "test-singlepass", + feature = "test-llvm", + feature = "test-native" + ), + ignore +)] fn trap_display_pretty() -> Result<()> { let store = get_store(); let wat = r#" @@ -184,9 +188,15 @@ RuntimeError: unreachable Ok(()) } -#[cfg(not(feature = "test-no-traps"))] #[test] -#[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)] +#[cfg_attr( + any( + feature = "test-singlepass", + feature = "test-llvm", + feature = "test-native" + ), + ignore +)] fn trap_display_multi_module() -> Result<()> { let store = get_store(); let wat = r#" @@ -390,9 +400,15 @@ fn mismatched_arguments() -> Result<()> { Ok(()) } -#[cfg(not(feature = "test-no-traps"))] #[test] -#[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)] +#[cfg_attr( + any( + feature = "test-singlepass", + feature = "test-llvm", + feature = "test-native" + ), + ignore +)] fn call_signature_mismatch() -> Result<()> { let store = get_store(); let binary = r#" @@ -422,9 +438,15 @@ RuntimeError: indirect call type mismatch Ok(()) } -#[cfg(not(feature = "test-no-traps"))] #[test] -#[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)] +#[cfg_attr( + any( + feature = "test-singlepass", + feature = "test-llvm", + feature = "test-native" + ), + ignore +)] fn start_trap_pretty() -> Result<()> { let store = get_store(); let wat = r#" @@ -455,8 +477,8 @@ RuntimeError: unreachable Ok(()) } -#[cfg(not(feature = "test-no-traps"))] #[test] +#[cfg_attr(feature = "test-native", ignore)] fn present_after_module_drop() -> Result<()> { let store = get_store(); let module = Module::new(&store, r#"(func (export "foo") unreachable)"#)?; diff --git a/tests/compilers/wast.rs b/tests/compilers/wast.rs index 1471a990a..c41b8da16 100644 --- a/tests/compilers/wast.rs +++ b/tests/compilers/wast.rs @@ -63,6 +63,20 @@ pub fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> { features.multi_value(false); let store = get_store(features, try_nan_canonicalization); let mut wast = Wast::new_with_spectest(store); + // `bulk-memory-operations/bulk.wast` checks for a message that + // specifies which element is uninitialized, but our traps don't + // shepherd that information out. + wast.allow_trap_message("uninitialized element 2", "uninitialized element"); + if cfg!(feature = "test-native") { + wast.allow_trap_message("call stack exhausted", "out of bounds memory access"); + wast.allow_trap_message("indirect call type mismatch", "call stack exhausted"); + wast.allow_trap_message("integer divide by zero", "call stack exhausted"); + wast.allow_trap_message("integer overflow", "call stack exhausted"); + wast.allow_trap_message("invalid conversion to integer", "call stack exhausted"); + wast.allow_trap_message("undefined element", "call stack exhausted"); + wast.allow_trap_message("uninitialized element", "call stack exhausted"); + wast.allow_trap_message("unreachable", "call stack exhausted"); + } if is_simd { // We allow this, so tests can be run properly for `simd_const` test. wast.allow_instantiation_failures(&[ diff --git a/tests/lib/wast/src/wast.rs b/tests/lib/wast/src/wast.rs index a53f4ce66..159d1a7b2 100644 --- a/tests/lib/wast/src/wast.rs +++ b/tests/lib/wast/src/wast.rs @@ -18,6 +18,9 @@ pub struct Wast { instances: HashMap, /// Allowed failures (ideally this should be empty) allowed_instantiation_failures: HashSet, + /// If the (expected from .wast, actual) message pair is in this list, + /// treat the strings as matching. + match_trap_messages: HashMap, /// If the current module was an allowed failure, we allow test to fail current_is_allowed_failure: bool, /// The wasm Store @@ -34,13 +37,14 @@ impl Wast { store, import_object, allowed_instantiation_failures: HashSet::new(), + match_trap_messages: HashMap::new(), current_is_allowed_failure: false, instances: HashMap::new(), fail_fast: true, } } - /// A list of instantiation failures to allow + /// A list of instantiation failures to allow. pub fn allow_instantiation_failures(&mut self, failures: &[&str]) { for &failure_str in failures.iter() { self.allowed_instantiation_failures @@ -48,6 +52,12 @@ impl Wast { } } + /// A list of alternative messages to permit for a trap failure. + pub fn allow_trap_message(&mut self, expected: &str, allowed: &str) { + self.match_trap_messages + .insert(expected.into(), allowed.into()); + } + /// Construct a new instance of `Wast` with the spectests imports. pub fn new_with_spectest(store: Store) -> Self { let import_object = spectest_importobject(&store); @@ -123,7 +133,7 @@ impl Wast { Ok(values) => bail!("expected trap, got {:?}", values), Err(t) => format!("{}", t), }; - if Self::matches_message_assert_trap(expected, &actual) { + if self.matches_message_assert_trap(expected, &actual) { return Ok(()); } bail!("expected '{}', got '{}'", expected, actual) @@ -406,12 +416,13 @@ impl Wast { } // Checks if the `assert_trap` message matches the expected one - fn matches_message_assert_trap(expected: &str, actual: &str) -> bool { + fn matches_message_assert_trap(&self, expected: &str, actual: &str) -> bool { actual.contains(expected) - // `bulk-memory-operations/bulk.wast` checks for a message that - // specifies which element is uninitialized, but our traps don't - // shepherd that information out. - || (expected.contains("uninitialized element 2") && actual.contains("uninitialized element")) + || self + .match_trap_messages + .get(expected) + .map(|alternative| actual.contains(alternative)) + .unwrap_or(false) } } From e0257785742d5b4de3123cda685d29da47a15da9 Mon Sep 17 00:00:00 2001 From: Nick Lewycky Date: Wed, 14 Oct 2020 16:20:21 -0700 Subject: [PATCH 45/49] Limit these disables to cranelift, add one test failure with link for llvm. --- tests/compilers/wast.rs | 2 +- tests/ignores.txt | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/compilers/wast.rs b/tests/compilers/wast.rs index c41b8da16..9d8b88b9a 100644 --- a/tests/compilers/wast.rs +++ b/tests/compilers/wast.rs @@ -67,7 +67,7 @@ pub fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> { // specifies which element is uninitialized, but our traps don't // shepherd that information out. wast.allow_trap_message("uninitialized element 2", "uninitialized element"); - if cfg!(feature = "test-native") { + if compiler == "cranelift" && cfg!(feature = "test-native") { wast.allow_trap_message("call stack exhausted", "out of bounds memory access"); wast.allow_trap_message("indirect call type mismatch", "call stack exhausted"); wast.allow_trap_message("integer divide by zero", "call stack exhausted"); diff --git a/tests/ignores.txt b/tests/ignores.txt index cdc45ddad..be05b5762 100644 --- a/tests/ignores.txt +++ b/tests/ignores.txt @@ -26,6 +26,9 @@ llvm::spec::skip_stack_guard_page on darwin cranelift::spec::linking on native llvm::spec::linking on native +# https://github.com/wasmerio/wasmer/issues/1722 +llvm::spec::skip_stack_guard_page on native + # Frontends ## WASI From 3a679b3976153c9549cd6f11ce17206429712d58 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 14 Oct 2020 17:08:46 -0700 Subject: [PATCH 46/49] Use GITHUB_PATH, GITHUB_ENV in CI The old style is being deprecated, see: https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/ --- .github/workflows/benchmark.yaml | 6 +++--- .github/workflows/coverage.yaml | 4 ++-- .github/workflows/main.yaml | 18 +++++++++--------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/benchmark.yaml b/.github/workflows/benchmark.yaml index f15dbcab2..0c42290f3 100644 --- a/.github/workflows/benchmark.yaml +++ b/.github/workflows/benchmark.yaml @@ -35,7 +35,7 @@ jobs: # that are needed during the build process. Additionally, this works # around a bug in the 'cache' action that causes directories outside of # the workspace dir to be saved/restored incorrectly. - run: echo "::set-env name=CARGO_HOME::$(pwd)/.cargo_home" + run: echo "CARGO_HOME=$(pwd)/.cargo_home" >> $GITHUB_ENV - name: Cache uses: actions/cache@master with: @@ -56,8 +56,8 @@ jobs: curl --proto '=https' --tlsv1.2 -sSf https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/clang+llvm-10.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz -L -o llvm.tar.xz mkdir -p /opt/llvm-10 tar xf llvm.tar.xz --strip-components=1 -C /opt/llvm-10 - echo ::add-path::/opt/llvm-10/bin - echo ::set-env name=LLVM_SYS_100_PREFIX::/opt/llvm-10 + echo '/opt/llvm-10/bin' >> $GITHUB_PATH + echo 'name=LLVM_SYS_100_PREFIX=/opt/llvm-10' >> $GITHUB_ENV - name: Install Python uses: actions/setup-python@v2 with: diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index cd86d13f2..2265909ac 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -29,8 +29,8 @@ jobs: curl --proto '=https' --tlsv1.2 -sSf https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/clang+llvm-10.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz -L -o llvm.tar.xz mkdir -p /opt/llvm-10 tar xf llvm.tar.xz --strip-components=1 -C /opt/llvm-10 - echo ::add-path::/opt/llvm-10/bin - echo ::set-env name=LLVM_SYS_100_PREFIX::/opt/llvm-10 + echo '/opt/llvm-10/bin' >> $GITHUB_PATH + echo 'LLVM_SYS_100_PREFIX=/opt/llvm-10' >> $GITHUB_ENV - name: Generate Coverage Report run: | cargo install cargo-tarpaulin diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 9318e1094..872f168f8 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -81,7 +81,7 @@ jobs: # that are needed during the build process. Additionally, this works # around a bug in the 'cache' action that causes directories outside of # the workspace dir to be saved/restored incorrectly. - run: echo "::set-env name=CARGO_HOME::$(pwd)/.cargo_home" + run: echo "CARGO_HOME=$(pwd)/.cargo_home" >> $GITHUB_ENV - name: Cache uses: actions/cache@master with: @@ -103,13 +103,13 @@ jobs: # key: cargo-sccache-bin-${{ env.CARGO_SCCACHE_VERSION }} # - name: Install sccache # run: | - # echo "::add-path::${{ runner.tool_cache }}/cargo-sccache/bin" + # echo "${{ runner.tool_cache }}/cargo-sccache/bin" >> $GITHUB_PATH # cargo install sccache --version ${{ env.CARGO_SCCACHE_VERSION }} --root ${{ runner.tool_cache }}/cargo-sccache # - name: Start sccache # run: | # ${{ runner.tool_cache }}/cargo-sccache/bin/sccache --start-server # ${{ runner.tool_cache }}/cargo-sccache/bin/sscache -s - # echo "::set-env name=RUSTC_WRAPPER::${{ runner.tool_cache }}/cargo-sccache/bin/sccache" + # echo "RUSTC_WRAPPER=${{ runner.tool_cache }}/cargo-sccache/bin/sccache" >> $GITHUB_ENV - name: Install LLVM (Windows) if: matrix.os == 'windows-latest' shell: cmd @@ -118,17 +118,17 @@ jobs: # run: | # curl --proto '=https' --tlsv1.2 -sSf https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/LLVM-10.0.0-win64.exe -L -o llvm-installer.exe # 7z x llvm-installer.exe -oC:/llvm-10 - # echo ::add-path::C:/llvm-10/bin - # echo ::set-env name=LLVM_SYS_100_PREFIX::C:/llvm-10 - # echo ::set-env name=LIBCLANG_PATH::C:/llvm-10/bin/libclang.dll + # echo C:/llvm-10/bin >> $GITHUB_PATH + # echo "LLVM_SYS_100_PREFIX=C:/llvm-10" >> $GITHUB_ENV + # echo "LIBCLANG_PATH=C:/llvm-10/bin/libclang.dll" >> $GITHUB_ENV - name: Install LLVM (Unix) if: matrix.os != 'windows-latest' run: | curl --proto '=https' --tlsv1.2 -sSf ${{ matrix.llvm_url }} -L -o llvm.tar.xz mkdir -p ${{ env.LLVM_DIR }} tar xf llvm.tar.xz --strip-components=1 -C ${{ env.LLVM_DIR }} - echo "::add-path::${{ env.LLVM_DIR }}/bin" - echo "::set-env name=LLVM_SYS_100_PREFIX::${{ env.LLVM_DIR }}" + echo "${{ env.LLVM_DIR }}/bin" >> $GITHUB_PATH + echo "LLVM_SYS_100_PREFIX=${{ env.LLVM_DIR }}" >> $GITHUB_ENV env: LLVM_DIR: ${{ github.workspace }}/llvm-10 - name: Set up dependencies for Mac OS @@ -242,7 +242,7 @@ jobs: path: ${{ runner.tool_cache }}/cargo-audit key: cargo-audit-bin-${{ env.CARGO_AUDIT_VERSION }} - run: | - echo "::add-path::${{ runner.tool_cache }}/cargo-audit/bin" + echo "${{ runner.tool_cache }}/cargo-audit/bin" >> $GITHUB_PATH - run: | cargo install cargo-audit --version ${{ env.CARGO_AUDIT_VERSION }} --root ${{ runner.tool_cache }}/cargo-audit cargo audit From 571e84625004b31cbbbbe2795c6da6e3b0c3d400 Mon Sep 17 00:00:00 2001 From: Nick Lewycky Date: Thu, 15 Oct 2020 11:36:25 -0700 Subject: [PATCH 47/49] Control which engines are tested by platform detection. --- Makefile | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 48c72eb17..7e260e00a 100644 --- a/Makefile +++ b/Makefile @@ -8,13 +8,19 @@ else UNAME_S := endif +# Which compilers we build. These have dependencies that may not on the system. compilers := +# Which engines we test. We always build all engines. +engines := + ifeq ($(ARCH), x86_64) + engines += jit # In X64, Cranelift is enabled compilers += cranelift # LLVM could be enabled if not in Windows ifneq ($(OS), Windows_NT) + engines += native # Singlepass doesn't work yet on Windows compilers += singlepass # Autodetect LLVM from llvm-config @@ -32,7 +38,12 @@ ifeq ($(ARCH), x86_64) endif endif +ifeq ($(ARCH), aarch64) + engines += native +endif + compilers := $(filter-out ,$(compilers)) +engines := $(filter-out ,$(engines)) ifneq ($(OS), Windows_NT) bold := $(shell tput bold) @@ -94,7 +105,7 @@ build-capi-llvm: # Testing # ########### -test: $(foreach compiler,$(compilers),test-$(compiler)-jit test-$(compiler)-native) test-packages test-examples test-deprecated +test: $(foreach engine,$(engines),$(foreach compiler,$(compilers),test-$(compiler)-$(engine))) test-packages test-examples test-deprecated # Singlepass and native engine don't work together, this rule does nothing. test-singlepass-native: From 6d3e1a59e29c7aae648d0bbfa896d25cf95e2223 Mon Sep 17 00:00:00 2001 From: Nick Lewycky Date: Thu, 15 Oct 2020 13:34:26 -0700 Subject: [PATCH 48/49] Add links to TODOs. --- tests/ignores.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/ignores.txt b/tests/ignores.txt index be05b5762..a55c8e043 100644 --- a/tests/ignores.txt +++ b/tests/ignores.txt @@ -22,8 +22,9 @@ singlepass on windows # Singlepass is not yet supported on Windows cranelift::spec::skip_stack_guard_page on darwin llvm::spec::skip_stack_guard_page on darwin -# TODO: traps in native engine +# TODO(https://github.com/wasmerio/wasmer/issues/1727): Traps in native engine cranelift::spec::linking on native +# TODO(https://github.com/wasmerio/wasmer/pull/1710): Library is dropped too soon. llvm::spec::linking on native # https://github.com/wasmerio/wasmer/issues/1722 From 69eef6e4905a18fd6756aacc3c16ae592ff53fcd Mon Sep 17 00:00:00 2001 From: Nick Lewycky Date: Thu, 15 Oct 2020 15:19:07 -0700 Subject: [PATCH 49/49] Enable cranelift on aarch64. Add comments to tests/ignores.txt explaining why they're disabled. Add a list of engines to test so that we don't test engine-native on aarch64. Disable tests in tests/compilers/traps.rs that fail with cranelift on aarch64. Disable tests in examples/ that use cranelift + native, on aarch64. --- Makefile | 15 +++++---------- examples/engine_headless.rs | 2 +- examples/engine_native.rs | 1 + lib/compiler-cranelift/Cargo.toml | 2 +- tests/compilers/traps.rs | 27 ++++++++++++++++++++++----- tests/ignores.txt | 15 +++++++++++++++ 6 files changed, 45 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index 7e260e00a..63a661f48 100644 --- a/Makefile +++ b/Makefile @@ -9,18 +9,14 @@ else endif # Which compilers we build. These have dependencies that may not on the system. -compilers := +compilers := cranelift # Which engines we test. We always build all engines. -engines := +engines := jit ifeq ($(ARCH), x86_64) - engines += jit - # In X64, Cranelift is enabled - compilers += cranelift # LLVM could be enabled if not in Windows ifneq ($(OS), Windows_NT) - engines += native # Singlepass doesn't work yet on Windows compilers += singlepass # Autodetect LLVM from llvm-config @@ -35,11 +31,10 @@ ifeq ($(ARCH), x86_64) compilers += llvm endif endif - endif -endif -ifeq ($(ARCH), aarch64) - engines += native + # Native engine doesn't work yet on Windows + engines += native + endif endif compilers := $(filter-out ,$(compilers)) diff --git a/examples/engine_headless.rs b/examples/engine_headless.rs index 817dccbed..f0d7694ee 100644 --- a/examples/engine_headless.rs +++ b/examples/engine_headless.rs @@ -147,7 +147,7 @@ fn main() -> Result<(), Box> { } #[test] -#[cfg(not(windows))] +#[cfg(not(any(windows, target_arch = "aarch64")))] fn test_engine_headless() -> Result<(), Box> { main() } diff --git a/examples/engine_native.rs b/examples/engine_native.rs index 47cd2d1fa..ea69744a4 100644 --- a/examples/engine_native.rs +++ b/examples/engine_native.rs @@ -87,6 +87,7 @@ fn main() -> Result<(), Box> { } #[test] +#[cfg(not(target_arch = "aarch64"))] fn test_engine_native() -> Result<(), Box> { main() } diff --git a/lib/compiler-cranelift/Cargo.toml b/lib/compiler-cranelift/Cargo.toml index 40ed587d8..0633afa46 100644 --- a/lib/compiler-cranelift/Cargo.toml +++ b/lib/compiler-cranelift/Cargo.toml @@ -15,7 +15,7 @@ edition = "2018" wasmer-compiler = { path = "../compiler", version = "1.0.0-alpha4", features = ["translator"], default-features = false } wasmer-vm = { path = "../vm", version = "1.0.0-alpha4" } wasmer-types = { path = "../wasmer-types", version = "1.0.0-alpha4", default-features = false, features = ["std"] } -cranelift-codegen = { version = "0.65", default-features = false } +cranelift-codegen = { version = "0.65", default-features = false, features = ["x86", "arm64"] } cranelift-frontend = { version = "0.65", default-features = false } tracing = "0.1" hashbrown = { version = "0.8", optional = true } diff --git a/tests/compilers/traps.rs b/tests/compilers/traps.rs index 7922511a0..7b1944ba3 100644 --- a/tests/compilers/traps.rs +++ b/tests/compilers/traps.rs @@ -38,7 +38,14 @@ fn test_trap_return() -> Result<()> { } #[test] -#[cfg_attr(any(feature = "test-singlepass", feature = "test-native"), ignore)] +#[cfg_attr( + any( + feature = "test-singlepass", + feature = "test-native", + target_arch = "aarch64", + ), + ignore +)] fn test_trap_trace() -> Result<()> { let store = get_store(); let wat = r#" @@ -118,7 +125,14 @@ fn test_trap_trace_cb() -> Result<()> { } #[test] -#[cfg_attr(any(feature = "test-singlepass", feature = "test-native"), ignore)] +#[cfg_attr( + any( + feature = "test-singlepass", + feature = "test-native", + target_arch = "aarch64", + ), + ignore +)] fn test_trap_stack_overflow() -> Result<()> { let store = get_store(); let wat = r#" @@ -153,7 +167,8 @@ fn test_trap_stack_overflow() -> Result<()> { any( feature = "test-singlepass", feature = "test-llvm", - feature = "test-native" + feature = "test-native", + target_arch = "aarch64", ), ignore )] @@ -193,7 +208,8 @@ RuntimeError: unreachable any( feature = "test-singlepass", feature = "test-llvm", - feature = "test-native" + feature = "test-native", + target_arch = "aarch64", ), ignore )] @@ -443,7 +459,8 @@ RuntimeError: indirect call type mismatch any( feature = "test-singlepass", feature = "test-llvm", - feature = "test-native" + feature = "test-native", + target_arch = "aarch64", ), ignore )] diff --git a/tests/ignores.txt b/tests/ignores.txt index a55c8e043..f8b426a02 100644 --- a/tests/ignores.txt +++ b/tests/ignores.txt @@ -30,6 +30,21 @@ llvm::spec::linking on native # https://github.com/wasmerio/wasmer/issues/1722 llvm::spec::skip_stack_guard_page on native +# Cranelift on arm failures +cranelift::spec::align on aarch64 +cranelift::spec::memory_trap on aarch64 +cranelift::spec::simd::simd_bit_shift on aarch64 +cranelift::spec::simd::simd_boolean on aarch64 +cranelift::spec::simd::simd_const on aarch64 +cranelift::spec::simd::simd_i16x8_arith on aarch64 +cranelift::spec::simd::simd_i16x8_sat_arith on aarch64 +cranelift::spec::simd::simd_i32x4_arith on aarch64 +cranelift::spec::simd::simd_i64x2_arith on aarch64 +cranelift::spec::simd::simd_i8x16_arith on aarch64 +cranelift::spec::simd::simd_i8x16_sat_arith on aarch64 +cranelift::spec::simd::simd_lane on aarch64 +cranelift::spec::skip_stack_guard_page on aarch64 + # Frontends ## WASI