2019-08-01 20:46:35 -06:00
#![ deny(
dead_code ,
2019-08-15 20:18:29 -06:00
nonstandard_style ,
2019-08-01 20:46:35 -06:00
unused_imports ,
2019-08-15 20:18:29 -06:00
unused_mut ,
2019-08-01 20:46:35 -06:00
unused_variables ,
unused_unsafe ,
unreachable_patterns
) ]
2018-11-06 15:51:01 +01:00
extern crate structopt ;
2018-10-11 21:29:36 +02:00
2019-02-22 11:42:30 -08:00
use std ::env ;
2019-11-22 19:02:57 +09:00
use std ::fs ::{ metadata , read_to_string , File } ;
2018-10-11 21:29:36 +02:00
use std ::io ;
use std ::io ::Read ;
2018-10-14 23:48:59 +02:00
use std ::path ::PathBuf ;
2018-10-11 21:29:36 +02:00
use std ::process ::exit ;
2019-04-11 12:44:03 -07:00
use std ::str ::FromStr ;
2018-10-11 21:29:36 +02:00
2019-07-31 23:17:42 -07:00
use std ::collections ::HashMap ;
2019-11-20 17:28:43 -08:00
use structopt ::{ clap , StructOpt } ;
2018-10-11 21:29:36 +02:00
2018-11-28 13:15:33 +08:00
use wasmer ::* ;
2019-11-21 23:31:10 +08:00
#[ cfg(feature = " backend-cranelift " ) ]
2019-04-11 14:34:54 -07:00
use wasmer_clif_backend ::CraneliftCompiler ;
2019-07-09 17:57:31 -07:00
#[ cfg(feature = " backend-llvm " ) ]
2019-10-08 11:23:18 -07:00
use wasmer_llvm_backend ::{ LLVMCompiler , LLVMOptions } ;
2019-04-22 16:45:36 -07:00
use wasmer_runtime ::{
2019-07-29 18:35:59 -07:00
cache ::{ Cache as BaseCache , FileSystemCache , WasmHash } ,
2019-11-21 10:57:04 -08:00
Value , VERSION ,
2019-04-22 16:45:36 -07:00
} ;
2019-08-10 02:44:44 +08:00
#[ cfg(feature = " managed " ) ]
use wasmer_runtime_core ::tiering ::{ run_tiering , InteractiveShellContext , ShellExitOperation } ;
2019-04-11 14:34:54 -07:00
use wasmer_runtime_core ::{
self ,
2019-07-25 23:33:30 -07:00
backend ::{ Backend , Compiler , CompilerConfig , Features , MemoryBoundCheckMode } ,
2019-07-08 15:46:28 -07:00
debug ,
2019-05-14 16:02:27 +08:00
loader ::{ Instance as LoadedInstance , LocalLoader } ,
2019-04-11 14:34:54 -07:00
} ;
2019-03-28 14:22:28 -07:00
#[ cfg(feature = " wasi " ) ]
2019-03-28 12:19:23 -07:00
use wasmer_wasi ;
2018-10-11 21:29:36 +02:00
#[ derive(Debug, StructOpt) ]
2019-09-06 16:42:10 -07:00
#[ structopt(name = " wasmer " , about = " Wasm execution runtime. " , author) ]
2018-10-14 23:47:35 +02:00
/// The options for the wasmer Command Line Interface
enum CLIOptions {
2019-09-12 20:56:11 -07:00
/// Run a WebAssembly file. Formats accepted: wasm, wat
2018-10-14 23:47:35 +02:00
#[ structopt(name = " run " ) ]
2018-10-14 23:48:59 +02:00
Run ( Run ) ,
2018-11-25 21:31:32 -08:00
2019-02-22 11:42:30 -08:00
/// Wasmer cache
#[ structopt(name = " cache " ) ]
Cache ( Cache ) ,
2019-04-03 16:52:37 -07:00
/// Validate a Web Assembly binary
#[ structopt(name = " validate " ) ]
Validate ( Validate ) ,
2018-11-25 21:31:32 -08:00
/// Update wasmer to the latest version
#[ structopt(name = " self-update " ) ]
SelfUpdate ,
2018-10-14 23:47:35 +02:00
}
2019-10-11 21:05:59 +08:00
#[ derive(Debug, StructOpt, Clone) ]
2019-07-25 23:33:30 -07:00
struct PrestandardFeatures {
/// Enable support for the SIMD proposal.
#[ structopt(long = " enable-simd " ) ]
simd : bool ,
2019-08-08 19:02:09 -07:00
/// Enable support for the threads proposal.
#[ structopt(long = " enable-threads " ) ]
threads : bool ,
2019-07-25 23:33:30 -07:00
/// Enable support for all pre-standard proposals.
#[ structopt(long = " enable-all " ) ]
all : bool ,
}
2019-11-16 11:25:33 -08:00
impl PrestandardFeatures {
/// Generate [`wabt::Features`] struct from CLI options
pub fn into_wabt_features ( & self ) -> wabt ::Features {
let mut features = wabt ::Features ::new ( ) ;
if self . simd | | self . all {
features . enable_simd ( ) ;
}
if self . threads | | self . all {
features . enable_threads ( ) ;
}
features . enable_sign_extension ( ) ;
features
}
/// Generate [`Features`] struct from CLI options
pub fn into_backend_features ( & self ) -> Features {
Features {
simd : self . simd | | self . all ,
threads : self . threads | | self . all ,
}
}
}
2019-08-08 19:42:41 -07:00
#[ cfg(feature = " backend-llvm " ) ]
#[ derive(Debug, StructOpt, Clone) ]
/// LLVM backend flags.
pub struct LLVMCLIOptions {
/// Emit LLVM IR before optimization pipeline.
#[ structopt(long = " llvm-pre-opt-ir " , parse(from_os_str)) ]
pre_opt_ir : Option < PathBuf > ,
/// Emit LLVM IR after optimization pipeline.
#[ structopt(long = " llvm-post-opt-ir " , parse(from_os_str)) ]
post_opt_ir : Option < PathBuf > ,
/// Emit LLVM generated native code object file.
2019-10-08 11:23:18 -07:00
#[ structopt(long = " llvm-object-file " , parse(from_os_str)) ]
2019-08-08 19:42:41 -07:00
obj_file : Option < PathBuf > ,
}
2019-10-11 21:05:59 +08:00
#[ derive(Debug, StructOpt, Clone) ]
2018-10-14 23:47:35 +02:00
struct Run {
2019-09-06 16:42:10 -07:00
/// Disable the cache
2019-02-22 12:01:03 -08:00
#[ structopt(long = " disable-cache " ) ]
disable_cache : bool ,
2018-10-11 21:29:36 +02:00
/// Input file
#[ structopt(parse(from_os_str)) ]
path : PathBuf ,
2018-12-06 12:32:53 +01:00
2019-11-21 23:31:10 +08:00
/// Name of the backend to use. (x86_64)
#[ cfg(target_arch = " x86_64 " ) ]
2019-04-11 14:34:54 -07:00
#[ structopt(
long = " backend " ,
2019-11-22 19:02:57 +09:00
default_value = " auto " ,
2019-09-06 16:42:10 -07:00
case_insensitive = true ,
possible_values = Backend ::variants ( ) ,
2019-04-11 14:34:54 -07:00
) ]
2019-04-11 12:44:03 -07:00
backend : Backend ,
2019-03-26 16:41:40 -07:00
2019-11-21 23:31:10 +08:00
/// Name of the backend to use. (aarch64)
#[ cfg(target_arch = " aarch64 " ) ]
#[ structopt(
long = " backend " ,
default_value = " singlepass " ,
case_insensitive = true ,
possible_values = Backend ::variants ( ) ,
) ]
backend : Backend ,
2019-11-14 12:07:53 -08:00
/// Invoke a specified function
#[ structopt(long = " invoke " , short = " i " ) ]
invoke : Option < String > ,
2019-04-19 09:26:47 -07:00
/// Emscripten symbol map
2019-04-10 18:23:25 -07:00
#[ structopt(long = " em-symbol-map " , parse(from_os_str), group = " emscripten " ) ]
2019-03-27 14:01:27 -07:00
em_symbol_map : Option < PathBuf > ,
2019-04-10 18:23:25 -07:00
2019-05-03 17:34:57 -07:00
/// Begin execution at the specified symbol
2019-05-05 12:13:35 -07:00
#[ structopt(long = " em-entrypoint " , group = " emscripten " ) ]
em_entrypoint : Option < String > ,
2019-05-03 17:34:57 -07:00
2019-04-19 12:48:29 -07:00
/// WASI pre-opened directory
#[ structopt(long = " dir " , multiple = true, group = " wasi " ) ]
2019-09-30 15:26:04 -07:00
pre_opened_directories : Vec < PathBuf > ,
2019-04-18 15:43:02 -07:00
2019-05-16 17:35:13 -07:00
/// Map a host directory to a different location for the wasm module
2019-05-20 16:39:02 -07:00
#[ structopt(long = " mapdir " , multiple = true) ]
2019-05-16 17:35:13 -07:00
mapped_dirs : Vec < String > ,
2019-05-30 11:58:52 -07:00
/// Pass custom environment variables
#[ structopt(long = " env " , multiple = true) ]
env_vars : Vec < String > ,
2019-05-16 17:35:13 -07:00
/// Custom code loader
2019-05-03 00:23:41 +08:00
#[ structopt(
long = " loader " ,
2019-09-06 16:42:10 -07:00
case_insensitive = true ,
possible_values = LoaderName ::variants ( ) ,
2019-05-03 00:23:41 +08:00
) ]
loader : Option < LoaderName > ,
2019-07-12 23:33:54 +08:00
/// Path to previously saved instance image to resume.
2019-08-09 04:26:17 +08:00
#[ cfg(feature = " managed " ) ]
2019-06-27 01:29:10 +08:00
#[ structopt(long = " resume " ) ]
resume : Option < String > ,
2019-08-10 02:43:21 +08:00
/// Optimized backends for higher tiers.
#[ cfg(feature = " managed " ) ]
#[ structopt(
long = " optimized-backends " ,
multiple = true ,
2019-09-06 16:42:10 -07:00
case_insensitive = true ,
possible_values = Backend ::variants ( ) ,
2019-08-10 02:43:21 +08:00
) ]
optimized_backends : Vec < Backend > ,
2019-07-12 23:33:54 +08:00
/// Whether or not state tracking should be disabled during compilation.
/// State tracking is necessary for tier switching and backtracing.
2019-10-22 23:14:42 -07:00
#[ structopt(long = " track-state " ) ]
track_state : bool ,
2019-07-12 23:33:54 +08:00
2019-10-11 21:05:59 +08:00
// Enable the CallTrace middleware.
#[ structopt(long = " call-trace " ) ]
call_trace : bool ,
2019-10-13 20:51:48 +08:00
// Enable the BlockTrace middleware.
#[ structopt(long = " block-trace " ) ]
block_trace : bool ,
2019-07-08 15:46:28 -07:00
/// The command name is a string that will override the first argument passed
/// to the wasm program. This is used in wapm to provide nicer output in
/// help commands and error messages of the running wasm program
2019-04-12 11:17:02 -07:00
#[ structopt(long = " command-name " , hidden = true) ]
command_name : Option < String > ,
2019-07-08 15:46:28 -07:00
/// A prehashed string, used to speed up start times by avoiding hashing the
/// wasm module. If the specified hash is not found, Wasmer will hash the module
/// as if no `cache-key` argument was passed.
#[ structopt(long = " cache-key " , hidden = true) ]
cache_key : Option < String > ,
2019-08-08 16:05:17 -07:00
#[ cfg(feature = " backend-llvm " ) ]
#[ structopt(flatten) ]
2019-08-08 19:42:41 -07:00
backend_llvm_options : LLVMCLIOptions ,
2019-08-08 16:05:17 -07:00
2019-07-25 23:33:30 -07:00
#[ structopt(flatten) ]
features : PrestandardFeatures ,
2019-04-11 12:44:03 -07:00
/// Application arguments
2019-09-06 16:42:10 -07:00
#[ structopt(name = " -- " , multiple = true) ]
2019-04-11 12:44:03 -07:00
args : Vec < String > ,
}
2019-12-02 16:53:15 -08:00
impl Run {
/// Used with the `invoke` argument
fn parse_args ( & self ) -> Result < Vec < Value > , String > {
let mut args : Vec < Value > = Vec ::new ( ) ;
for arg in self . args . iter ( ) {
let x = arg . as_str ( ) . parse ( ) . map_err ( | _ | {
format! (
" Can't parse the provided argument {:?} as a integer " ,
arg . as_str ( )
)
} ) ? ;
args . push ( Value ::I32 ( x ) ) ;
}
Ok ( args )
}
}
2019-05-03 00:23:41 +08:00
#[ allow(dead_code) ]
#[ derive(Debug, Copy, Clone) ]
enum LoaderName {
Local ,
2019-07-12 23:02:57 -05:00
#[ cfg(feature = " loader-kernel " ) ]
2019-05-03 23:07:07 -07:00
Kernel ,
2019-05-03 00:23:41 +08:00
}
impl LoaderName {
pub fn variants ( ) -> & 'static [ & 'static str ] {
& [
" local " ,
2019-07-12 23:02:57 -05:00
#[ cfg(feature = " loader-kernel " ) ]
2019-05-03 23:07:07 -07:00
" kernel " ,
2019-05-03 00:23:41 +08:00
]
}
}
impl FromStr for LoaderName {
type Err = String ;
fn from_str ( s : & str ) -> Result < LoaderName , String > {
match s . to_lowercase ( ) . as_str ( ) {
" local " = > Ok ( LoaderName ::Local ) ,
2019-07-12 23:02:57 -05:00
#[ cfg(feature = " loader-kernel " ) ]
2019-05-03 23:07:07 -07:00
" kernel " = > Ok ( LoaderName ::Kernel ) ,
2019-05-03 00:23:41 +08:00
_ = > Err ( format! ( " The loader {} doesn't exist " , s ) ) ,
}
}
}
2019-02-22 11:42:30 -08:00
#[ derive(Debug, StructOpt) ]
enum Cache {
2019-03-19 12:13:23 -07:00
/// Clear the cache
2019-02-22 11:42:30 -08:00
#[ structopt(name = " clean " ) ]
Clean ,
2019-03-19 12:13:23 -07:00
/// Display the location of the cache
2019-02-22 11:42:30 -08:00
#[ structopt(name = " dir " ) ]
Dir ,
}
2019-04-03 16:52:37 -07:00
#[ derive(Debug, StructOpt) ]
struct Validate {
/// Input file
#[ structopt(parse(from_os_str)) ]
path : PathBuf ,
2019-07-25 23:33:30 -07:00
#[ structopt(flatten) ]
features : PrestandardFeatures ,
2019-04-03 16:52:37 -07:00
}
2018-10-14 23:48:59 +02:00
/// Read the contents of a file
2018-11-15 00:50:54 -08:00
fn read_file_contents ( path : & PathBuf ) -> Result < Vec < u8 > , io ::Error > {
2018-10-11 21:29:36 +02:00
let mut buffer : Vec < u8 > = Vec ::new ( ) ;
let mut file = File ::open ( path ) ? ;
file . read_to_end ( & mut buffer ) ? ;
2018-12-28 13:59:55 +01:00
// We force to close the file
drop ( file ) ;
2018-10-11 21:29:36 +02:00
Ok ( buffer )
}
2019-02-22 11:42:30 -08:00
fn get_cache_dir ( ) -> PathBuf {
match env ::var ( " WASMER_CACHE_DIR " ) {
2019-07-29 17:33:50 -07:00
Ok ( dir ) = > {
let mut path = PathBuf ::from ( dir ) ;
2019-07-29 18:31:57 -07:00
path . push ( VERSION ) ;
2019-07-29 17:33:50 -07:00
path
2019-07-29 17:54:27 -07:00
}
2019-02-22 11:42:30 -08:00
Err ( _ ) = > {
// We use a temporal directory for saving cache files
let mut temp_dir = env ::temp_dir ( ) ;
temp_dir . push ( " wasmer " ) ;
2019-07-29 18:31:57 -07:00
temp_dir . push ( VERSION ) ;
2019-02-22 11:42:30 -08:00
temp_dir
}
}
}
2019-05-30 11:58:52 -07:00
fn get_mapped_dirs ( input : & [ String ] ) -> Result < Vec < ( String , PathBuf ) > , String > {
let mut md = vec! [ ] ;
for entry in input . iter ( ) {
if let [ alias , real_dir ] = entry . split ( ':' ) . collect ::< Vec < & str > > ( ) [ .. ] {
let pb = PathBuf ::from ( & real_dir ) ;
if let Ok ( pb_metadata ) = pb . metadata ( ) {
if ! pb_metadata . is_dir ( ) {
return Err ( format! (
" \" {} \" exists, but it is not a directory " ,
& real_dir
) ) ;
}
} else {
return Err ( format! ( " Directory \" {} \" does not exist " , & real_dir ) ) ;
}
md . push ( ( alias . to_string ( ) , pb ) ) ;
continue ;
}
return Err ( format! (
" Directory mappings must consist of two paths separate by a colon. Found {} " ,
& entry
) ) ;
}
Ok ( md )
}
2019-11-21 10:57:04 -08:00
#[ cfg(feature = " wasi " ) ]
2019-05-30 11:58:52 -07:00
fn get_env_var_args ( input : & [ String ] ) -> Result < Vec < ( & str , & str ) > , String > {
let mut ev = vec! [ ] ;
for entry in input . iter ( ) {
if let [ env_var , value ] = entry . split ( '=' ) . collect ::< Vec < & str > > ( ) [ .. ] {
ev . push ( ( env_var , value ) ) ;
} else {
return Err ( format! (
" Env vars must be of the form <var_name>=<value>. Found {} " ,
& entry
) ) ;
}
}
Ok ( ev )
}
2019-11-21 10:57:04 -08:00
/// Helper function for `execute_wasm` (the `Run` command)
#[ cfg(feature = " wasi " ) ]
fn execute_wasi (
wasi_version : wasmer_wasi ::WasiVersion ,
options : & Run ,
env_vars : Vec < ( & str , & str ) > ,
module : wasmer_runtime_core ::Module ,
mapped_dirs : Vec < ( String , PathBuf ) > ,
2019-11-22 09:09:01 +08:00
_wasm_binary : & [ u8 ] ,
2019-11-21 10:57:04 -08:00
) -> Result < ( ) , String > {
let args = if let Some ( cn ) = & options . command_name {
[ cn . clone ( ) ]
} else {
[ options . path . to_str ( ) . unwrap ( ) . to_owned ( ) ]
}
. iter ( )
. chain ( options . args . iter ( ) )
. cloned ( )
. map ( | arg | arg . into_bytes ( ) )
. collect ( ) ;
let envs = env_vars
. into_iter ( )
. map ( | ( k , v ) | format! ( " {} = {} " , k , v ) . into_bytes ( ) )
. collect ( ) ;
let preopened_files = options . pre_opened_directories . clone ( ) ;
let import_object = wasmer_wasi ::generate_import_object_for_version (
wasi_version ,
args ,
envs ,
preopened_files ,
mapped_dirs ,
) ;
#[ allow(unused_mut) ] // mut used in feature
let mut instance = module
. instantiate ( & import_object )
. map_err ( | e | format! ( " Can't instantiate WASI module: {:?} " , e ) ) ? ;
let start : wasmer_runtime ::Func < ( ) , ( ) > =
instance . func ( " _start " ) . map_err ( | e | format! ( " {:?} " , e ) ) ? ;
#[ cfg(feature = " managed " ) ]
{
let start_raw : extern " C " fn ( & mut wasmer_runtime_core ::vm ::Ctx ) =
unsafe { ::std ::mem ::transmute ( start . get_vm_func ( ) ) } ;
unsafe {
run_tiering (
module . info ( ) ,
2019-11-22 09:09:01 +08:00
& _wasm_binary ,
2019-11-21 10:57:04 -08:00
if let Some ( ref path ) = options . resume {
let mut f = File ::open ( path ) . unwrap ( ) ;
let mut out : Vec < u8 > = vec! [ ] ;
f . read_to_end ( & mut out ) . unwrap ( ) ;
Some (
wasmer_runtime_core ::state ::InstanceImage ::from_bytes ( & out )
. expect ( " failed to decode image " ) ,
)
} else {
None
} ,
& import_object ,
start_raw ,
& mut instance ,
2019-11-22 09:09:01 +08:00
options . backend ,
2019-11-21 10:57:04 -08:00
options
. optimized_backends
. iter ( )
2019-11-22 09:09:01 +08:00
. map (
| & backend | -> ( Backend , Box < dyn Fn ( ) -> Box < dyn Compiler > + Send > ) {
let options = options . clone ( ) ;
(
backend ,
Box ::new ( move | | {
get_compiler_by_backend ( backend , & options ) . unwrap ( )
} ) ,
)
} ,
)
2019-11-21 10:57:04 -08:00
. collect ( ) ,
interactive_shell ,
) ?
} ;
}
#[ cfg(not(feature = " managed " )) ]
{
use wasmer_runtime ::error ::RuntimeError ;
2019-11-22 09:09:01 +08:00
#[ cfg(unix) ]
use wasmer_runtime_core ::{
fault ::{ pop_code_version , push_code_version } ,
state ::CodeVersion ,
} ;
let result ;
#[ cfg(unix) ]
2019-12-02 16:53:15 -08:00
let cv_pushed = if let Some ( msm ) = instance . module . runnable_module . get_module_state_map ( ) {
push_code_version ( CodeVersion {
baseline : true ,
msm : msm ,
base : instance . module . runnable_module . get_code ( ) . unwrap ( ) . as_ptr ( ) as usize ,
backend : options . backend ,
} ) ;
true
} else {
false
} ;
if let Some ( invoke_fn ) = options . invoke . as_ref ( ) {
eprintln! ( " WARNING: Invoking a function with WASI is not officially supported in the WASI standard yet. Use this feature at your own risk, things may not work! " ) ;
let args = options . parse_args ( ) ? ;
let invoke_result = instance
. dyn_func ( invoke_fn )
. map_err ( | e | format! ( " Invoke failed: {:?} " , e ) ) ?
. call ( & args )
. map_err ( | e | format! ( " Calling invoke fn failed: {:?} " , e ) ) ? ;
println! ( " {} returned {:?} " , invoke_fn , invoke_result ) ;
return Ok ( ( ) ) ;
} else {
2019-11-22 09:09:01 +08:00
result = start . call ( ) ;
2019-12-02 16:53:15 -08:00
}
#[ cfg(unix) ]
{
2019-11-22 09:09:01 +08:00
if cv_pushed {
pop_code_version ( ) . unwrap ( ) ;
}
}
2019-11-21 10:57:04 -08:00
if let Err ( ref err ) = result {
match err {
RuntimeError ::Trap { msg } = > return Err ( format! ( " wasm trap occured: {} " , msg ) ) ,
RuntimeError ::Error { data } = > {
if let Some ( error_code ) = data . downcast_ref ::< wasmer_wasi ::ExitCode > ( ) {
std ::process ::exit ( error_code . code as i32 )
}
}
}
return Err ( format! ( " error: {:?} " , err ) ) ;
}
}
Ok ( ( ) )
}
2019-01-19 00:28:41 -06:00
/// Execute a wasm/wat file
2018-12-06 12:32:53 +01:00
fn execute_wasm ( options : & Run ) -> Result < ( ) , String > {
2019-02-25 11:53:15 -08:00
let disable_cache = options . disable_cache ;
2019-02-25 11:46:48 -08:00
2019-05-30 11:58:52 -07:00
let mapped_dirs = get_mapped_dirs ( & options . mapped_dirs [ .. ] ) ? ;
2019-11-21 10:57:04 -08:00
#[ cfg(feature = " wasi " ) ]
2019-05-30 11:58:52 -07:00
let env_vars = get_env_var_args ( & options . env_vars [ .. ] ) ? ;
2018-12-06 12:32:53 +01:00
let wasm_path = & options . path ;
let mut wasm_binary : Vec < u8 > = read_file_contents ( wasm_path ) . map_err ( | err | {
2018-11-15 13:31:37 -08:00
format! (
" Can't read the file {}: {} " ,
wasm_path . as_os_str ( ) . to_string_lossy ( ) ,
err
)
} ) ? ;
2018-12-06 12:32:53 +01:00
2019-03-27 14:01:27 -07:00
let em_symbol_map = if let Some ( em_symbol_map_path ) = options . em_symbol_map . clone ( ) {
let em_symbol_map_content : String = read_to_string ( & em_symbol_map_path )
2019-03-26 16:41:40 -07:00
. map_err ( | err | {
format! (
" Can't read symbol map file {}: {} " ,
2019-03-27 14:01:27 -07:00
em_symbol_map_path . as_os_str ( ) . to_string_lossy ( ) ,
2019-03-26 16:41:40 -07:00
err ,
)
} ) ?
. to_owned ( ) ;
2019-03-27 14:01:27 -07:00
let mut em_symbol_map = HashMap ::new ( ) ;
for line in em_symbol_map_content . lines ( ) {
2019-03-26 16:41:40 -07:00
let mut split = line . split ( ':' ) ;
let num_str = if let Some ( ns ) = split . next ( ) {
ns
} else {
2019-05-30 11:58:52 -07:00
return Err (
2019-03-26 16:41:40 -07:00
" Can't parse symbol map (expected each entry to be of the form: `0:func_name`) "
2019-05-30 11:58:52 -07:00
. to_string ( ) ,
) ;
2019-03-26 16:41:40 -07:00
} ;
let num : u32 = num_str . parse ::< u32 > ( ) . map_err ( | err | {
format! (
" Failed to parse {} as a number in symbol map: {} " ,
num_str , err
)
} ) ? ;
let name_str : String = if let Some ( name_str ) = split . next ( ) {
name_str
} else {
2019-05-30 11:58:52 -07:00
return Err (
2019-03-26 16:41:40 -07:00
" Can't parse symbol map (expected each entry to be of the form: `0:func_name`) "
2019-05-30 11:58:52 -07:00
. to_string ( ) ,
) ;
2019-03-26 16:41:40 -07:00
}
. to_owned ( ) ;
2019-03-27 14:01:27 -07:00
em_symbol_map . insert ( num , name_str ) ;
2019-03-26 16:41:40 -07:00
}
2019-03-27 14:01:27 -07:00
Some ( em_symbol_map )
2019-03-26 16:41:40 -07:00
} else {
None
} ;
2019-07-25 23:33:30 -07:00
// Don't error on --enable-all for other backends.
if options . features . simd & & options . backend ! = Backend ::LLVM {
return Err ( " SIMD is only supported in the LLVM backend for now " . to_string ( ) ) ;
}
2019-01-24 15:09:56 -08:00
if ! utils ::is_wasm_binary ( & wasm_binary ) {
2019-11-16 11:25:33 -08:00
let features = options . features . into_wabt_features ( ) ;
2019-07-02 14:32:41 -07:00
wasm_binary = wabt ::wat2wasm_with_features ( wasm_binary , features )
2019-01-18 10:54:16 -08:00
. map_err ( | e | format! ( " Can't convert from wast to wasm: {:?} " , e ) ) ? ;
2018-10-11 21:29:36 +02:00
}
2018-12-06 12:32:53 +01:00
2019-10-11 21:05:59 +08:00
let compiler : Box < dyn Compiler > = match get_compiler_by_backend ( options . backend , options ) {
2019-07-30 22:25:58 +08:00
Some ( x ) = > x ,
None = > return Err ( " the requested backend is not enabled " . into ( ) ) ,
2019-04-11 14:34:54 -07:00
} ;
2019-10-08 11:23:18 -07:00
#[ cfg(feature = " backend-llvm " ) ]
{
2019-10-08 11:29:03 -07:00
if options . backend = = Backend ::LLVM {
2019-10-08 11:23:18 -07:00
let options = options . backend_llvm_options . clone ( ) ;
unsafe {
wasmer_llvm_backend ::GLOBAL_OPTIONS = LLVMOptions {
pre_opt_ir : options . pre_opt_ir ,
post_opt_ir : options . post_opt_ir ,
obj_file : options . obj_file ,
}
}
}
}
2019-10-08 11:29:03 -07:00
2019-10-22 23:14:42 -07:00
let track_state = options . track_state ;
2019-07-12 23:33:54 +08:00
2019-07-12 23:02:57 -05:00
#[ cfg(feature = " loader-kernel " ) ]
2019-05-06 07:15:30 -07:00
let is_kernel_loader = if let Some ( LoaderName ::Kernel ) = options . loader {
true
} else {
false
} ;
2019-07-12 23:02:57 -05:00
#[ cfg(not(feature = " loader-kernel " )) ]
2019-05-14 16:02:27 +08:00
let is_kernel_loader = false ;
2019-05-06 07:15:30 -07:00
let module = if is_kernel_loader {
webassembly ::compile_with_config_with (
& wasm_binary [ .. ] ,
CompilerConfig {
2019-07-30 22:25:58 +08:00
symbol_map : em_symbol_map . clone ( ) ,
2019-05-08 10:25:29 -07:00
memory_bound_check_mode : MemoryBoundCheckMode ::Disable ,
2019-05-06 07:15:30 -07:00
enforce_stack_check : true ,
2019-07-12 23:33:54 +08:00
track_state ,
2019-11-16 11:25:33 -08:00
features : options . features . into_backend_features ( ) ,
2019-11-13 12:31:50 -08:00
.. Default ::default ( )
2019-05-06 07:15:30 -07:00
} ,
& * compiler ,
2019-05-14 16:04:08 +08:00
)
. map_err ( | e | format! ( " Can't compile module: {:?} " , e ) ) ?
2019-05-06 07:15:30 -07:00
} else if disable_cache {
webassembly ::compile_with_config_with (
& wasm_binary [ .. ] ,
CompilerConfig {
2019-07-30 22:25:58 +08:00
symbol_map : em_symbol_map . clone ( ) ,
2019-07-12 23:33:54 +08:00
track_state ,
2019-11-16 11:25:33 -08:00
features : options . features . into_backend_features ( ) ,
2019-05-06 07:15:30 -07:00
.. Default ::default ( )
} ,
& * compiler ,
2019-05-14 16:04:08 +08:00
)
. map_err ( | e | format! ( " Can't compile module: {:?} " , e ) ) ?
2019-05-06 07:15:30 -07:00
} else {
2019-02-22 12:01:03 -08:00
// If we have cache enabled
let wasmer_cache_dir = get_cache_dir ( ) ;
// We create a new cache instance.
// It could be possible to use any other kinds of caching, as long as they
// implement the Cache trait (with save and load functions)
let mut cache = unsafe {
FileSystemCache ::new ( wasmer_cache_dir ) . map_err ( | e | format! ( " Cache error: {:?} " , e ) ) ?
} ;
2019-07-30 22:25:58 +08:00
let mut load_cache_key = | | -> Result < _ , String > {
2019-07-08 15:46:28 -07:00
if let Some ( ref prehashed_cache_key ) = options . cache_key {
2019-07-08 17:05:54 -07:00
if let Ok ( module ) =
WasmHash ::decode ( prehashed_cache_key ) . and_then ( | prehashed_key | {
cache . load_with_backend ( prehashed_key , options . backend )
} )
2019-07-08 15:46:28 -07:00
{
debug! ( " using prehashed key: {} " , prehashed_cache_key ) ;
return Ok ( module ) ;
}
2019-02-22 12:01:03 -08:00
}
2019-07-08 15:46:28 -07:00
// We generate a hash for the given binary, so we can use it as key
// for the Filesystem cache
2019-07-08 17:05:54 -07:00
let hash = WasmHash ::generate ( & wasm_binary ) ;
2019-07-08 15:46:28 -07:00
// cache.load will return the Module if it's able to deserialize it properly, and an error if:
// * The file is not found
// * The file exists, but it's corrupted or can't be converted to a module
2019-07-08 17:05:54 -07:00
match cache . load_with_backend ( hash , options . backend ) {
2019-07-08 15:46:28 -07:00
Ok ( module ) = > {
// We are able to load the module from cache
Ok ( module )
}
Err ( _ ) = > {
let module = webassembly ::compile_with_config_with (
& wasm_binary [ .. ] ,
CompilerConfig {
2019-07-30 22:25:58 +08:00
symbol_map : em_symbol_map . clone ( ) ,
2019-07-12 23:33:54 +08:00
track_state ,
2019-11-16 11:25:33 -08:00
features : options . features . into_backend_features ( ) ,
2019-07-08 15:46:28 -07:00
.. Default ::default ( )
} ,
& * compiler ,
)
. map_err ( | e | format! ( " Can't compile module: {:?} " , e ) ) ? ;
// We try to save the module into a cache file
cache . store ( hash , module . clone ( ) ) . unwrap_or_default ( ) ;
Ok ( module )
}
2019-02-22 12:01:03 -08:00
}
2019-07-08 15:46:28 -07:00
} ;
load_cache_key ( ) ?
2019-02-22 11:42:30 -08:00
} ;
2018-12-10 20:15:41 -08:00
2019-05-03 00:23:41 +08:00
if let Some ( loader ) = options . loader {
2019-05-04 08:28:13 -07:00
let mut import_object = wasmer_runtime_core ::import ::ImportObject ::new ( ) ;
import_object . allow_missing_functions = true ; // Import initialization might be left to the loader.
2019-05-03 00:23:41 +08:00
let instance = module
. instantiate ( & import_object )
2019-09-22 18:23:22 -07:00
. map_err ( | e | format! ( " Can't instantiate loader module: {:?} " , e ) ) ? ;
2019-05-03 00:23:41 +08:00
2019-09-25 21:52:45 +02:00
let mut args : Vec < Value > = Vec ::new ( ) ;
for arg in options . args . iter ( ) {
let x = arg . as_str ( ) . parse ( ) . map_err ( | _ | {
format! (
2019-05-15 10:35:26 -07:00
" Can't parse the provided argument {:?} as a integer " ,
2019-09-25 21:52:45 +02:00
arg . as_str ( )
)
} ) ? ;
args . push ( Value ::I32 ( x ) ) ;
}
2019-05-15 10:35:26 -07:00
let index = instance
. resolve_func ( " _start " )
. expect ( " The loader requires a _start function to be present in the module " ) ;
2019-08-08 19:42:41 -07:00
let mut ins : Box < dyn LoadedInstance < Error = String > > = match loader {
2019-05-15 10:35:26 -07:00
LoaderName ::Local = > Box ::new (
instance
. load ( LocalLoader )
. expect ( " Can't use the local loader " ) ,
) ,
2019-07-12 23:02:57 -05:00
#[ cfg(feature = " loader-kernel " ) ]
2019-05-15 10:35:26 -07:00
LoaderName ::Kernel = > Box ::new (
instance
. load ( ::wasmer_kernel_loader ::KernelLoader )
. expect ( " Can't use the kernel loader " ) ,
) ,
2019-05-03 23:07:07 -07:00
} ;
println! ( " {:?} " , ins . call ( index , & args ) ) ;
2019-05-14 16:04:08 +08:00
return Ok ( ( ) ) ;
2019-05-03 00:23:41 +08:00
}
2019-03-28 12:19:23 -07:00
// TODO: refactor this
2019-04-22 16:45:36 -07:00
if wasmer_emscripten ::is_emscripten_module ( & module ) {
2019-08-16 09:12:46 -06:00
let mut emscripten_globals = wasmer_emscripten ::EmscriptenGlobals ::new ( & module ) ? ;
2019-04-22 16:45:36 -07:00
let import_object = wasmer_emscripten ::generate_emscripten_env ( & mut emscripten_globals ) ;
let mut instance = module
. instantiate ( & import_object )
2019-09-22 18:23:22 -07:00
. map_err ( | e | format! ( " Can't instantiate emscripten module: {:?} " , e ) ) ? ;
2019-04-22 16:45:36 -07:00
wasmer_emscripten ::run_emscripten_instance (
& module ,
& mut instance ,
2019-07-06 22:05:45 -07:00
& mut emscripten_globals ,
2019-04-22 16:45:36 -07:00
if let Some ( cn ) = & options . command_name {
cn
} else {
options . path . to_str ( ) . unwrap ( )
} ,
options . args . iter ( ) . map ( | arg | arg . as_str ( ) ) . collect ( ) ,
2019-05-05 12:13:35 -07:00
options . em_entrypoint . clone ( ) ,
2019-05-24 18:00:07 -07:00
mapped_dirs ,
2019-01-19 00:28:41 -06:00
)
2019-04-22 16:45:36 -07:00
. map_err ( | e | format! ( " {:?} " , e ) ) ? ;
2018-12-15 00:46:11 -06:00
} else {
2019-11-21 10:57:04 -08:00
#[ cfg(feature = " wasi " ) ]
2019-11-12 12:54:28 -08:00
let wasi_version = wasmer_wasi ::get_wasi_version ( & module ) ;
2019-11-21 10:57:04 -08:00
#[ cfg(feature = " wasi " ) ]
let is_wasi = wasi_version . is_some ( ) ;
#[ cfg(not(feature = " wasi " )) ]
let is_wasi = false ;
if is_wasi {
#[ cfg(feature = " wasi " ) ]
execute_wasi (
wasi_version . unwrap ( ) ,
options ,
env_vars ,
module ,
2019-05-16 17:35:13 -07:00
mapped_dirs ,
2019-11-22 09:09:01 +08:00
& wasm_binary ,
2019-11-21 10:57:04 -08:00
) ? ;
2019-04-12 11:17:02 -07:00
} else {
2019-04-22 16:45:36 -07:00
let import_object = wasmer_runtime_core ::import ::ImportObject ::new ( ) ;
let instance = module
. instantiate ( & import_object )
. map_err ( | e | format! ( " Can't instantiate module: {:?} " , e ) ) ? ;
2019-12-02 16:53:15 -08:00
let args = options . parse_args ( ) ? ;
2019-09-25 21:52:45 +02:00
2019-11-15 14:04:48 -08:00
let invoke_fn = match options . invoke . as_ref ( ) {
Some ( fun ) = > fun ,
_ = > " main " ,
} ;
2019-11-17 05:15:10 +08:00
let result = instance
2019-11-14 12:07:53 -08:00
. dyn_func ( & invoke_fn )
2019-06-02 15:51:52 -05:00
. map_err ( | e | format! ( " {:?} " , e ) ) ?
. call ( & args )
. map_err ( | e | format! ( " {:?} " , e ) ) ? ;
2019-09-17 00:54:34 +08:00
println! ( " main() returned: {:?} " , result ) ;
2019-04-22 16:45:36 -07:00
}
}
2019-01-26 13:42:38 -06:00
2019-01-19 00:28:41 -06:00
Ok ( ( ) )
2018-10-11 21:29:36 +02:00
}
2019-08-09 04:26:17 +08:00
#[ cfg(feature = " managed " ) ]
2019-06-27 00:41:07 +08:00
fn interactive_shell ( mut ctx : InteractiveShellContext ) -> ShellExitOperation {
use std ::io ::Write ;
let mut stdout = ::std ::io ::stdout ( ) ;
let stdin = ::std ::io ::stdin ( ) ;
loop {
print! ( " Wasmer> " ) ;
stdout . flush ( ) . unwrap ( ) ;
let mut line = String ::new ( ) ;
stdin . read_line ( & mut line ) . unwrap ( ) ;
let mut parts = line . split ( " " ) . filter ( | x | x . len ( ) > 0 ) . map ( | x | x . trim ( ) ) ;
let cmd = parts . next ( ) ;
if cmd . is_none ( ) {
println! ( " Command required " ) ;
continue ;
}
let cmd = cmd . unwrap ( ) ;
match cmd {
" snapshot " = > {
let path = parts . next ( ) ;
if path . is_none ( ) {
println! ( " Usage: snapshot [out_path] " ) ;
continue ;
}
let path = path . unwrap ( ) ;
if let Some ( ref image ) = ctx . image {
let buf = image . to_bytes ( ) ;
let mut f = match File ::create ( path ) {
Ok ( x ) = > x ,
Err ( e ) = > {
println! ( " Cannot open output file at {} : {:?} " , path , e ) ;
continue ;
}
} ;
if let Err ( e ) = f . write_all ( & buf ) {
println! ( " Cannot write to output file at {} : {:?} " , path , e ) ;
continue ;
}
println! ( " Done " ) ;
} else {
println! ( " Program state not available " ) ;
}
}
" continue " | " c " = > {
2019-08-16 13:08:10 -07:00
if let Some ( image ) = ctx . image . take ( ) {
return ShellExitOperation ::ContinueWith ( image ) ;
2019-07-30 22:25:58 +08:00
} else {
2019-08-16 13:08:10 -07:00
println! ( " Program state not available, cannot continue execution " ) ;
2019-06-27 00:41:07 +08:00
}
}
" backtrace " | " bt " = > {
if let Some ( ref image ) = ctx . image {
2019-09-15 03:21:04 +02:00
println! ( " {} " , image . execution_state . output ( ) ) ;
2019-06-27 00:41:07 +08:00
} else {
println! ( " State not available " ) ;
}
}
" exit " | " quit " = > {
exit ( 0 ) ;
}
2019-06-27 17:54:06 +08:00
" " = > { }
2019-06-27 00:41:07 +08:00
_ = > {
println! ( " Unknown command: {} " , cmd ) ;
}
}
}
}
2019-11-22 19:02:57 +09:00
fn update_backend ( options : & mut Run ) {
let binary_size = match metadata ( & options . path ) {
Ok ( wasm_binary ) = > wasm_binary . len ( ) ,
Err ( _e ) = > 0 ,
} ;
// Update backend when a backend flag is `auto`.
// Use the Singlepass backend if it's enabled and the file provided is larger
// than 10MiB (10485760 bytes), or it's enabled and the target architecture
// is AArch64. Otherwise, use the Cranelift backend.
if options . backend = = Backend ::Auto {
if Backend ::variants ( ) . contains ( & Backend ::Singlepass . to_string ( ) )
& & ( binary_size > 10485760 | | cfg! ( target_arch = " aarch64 " ) )
{
options . backend = Backend ::Singlepass ;
} else {
options . backend = Backend ::Cranelift ;
}
}
}
fn run ( options : & mut Run ) {
update_backend ( options ) ;
match execute_wasm ( options ) {
2018-10-11 21:29:36 +02:00
Ok ( ( ) ) = > { }
Err ( message ) = > {
2019-09-22 18:35:26 -07:00
eprintln! ( " Error: {} " , message ) ;
2018-10-11 21:29:36 +02:00
exit ( 1 ) ;
}
}
}
2018-10-14 23:47:35 +02:00
2019-04-03 16:52:37 -07:00
fn validate_wasm ( validate : Validate ) -> Result < ( ) , String > {
let wasm_path = validate . path ;
let wasm_path_as_str = wasm_path . to_str ( ) . unwrap ( ) ;
let wasm_binary : Vec < u8 > = read_file_contents ( & wasm_path ) . map_err ( | err | {
format! (
" Can't read the file {}: {} " ,
wasm_path . as_os_str ( ) . to_string_lossy ( ) ,
err
)
} ) ? ;
if ! utils ::is_wasm_binary ( & wasm_binary ) {
return Err ( format! (
" Cannot recognize \" {} \" as a WASM binary " ,
wasm_path_as_str ,
) ) ;
}
2019-07-25 23:33:30 -07:00
wasmer_runtime_core ::validate_and_report_errors_with_features (
& wasm_binary ,
2019-11-16 11:25:33 -08:00
validate . features . into_backend_features ( ) ,
2019-07-25 23:33:30 -07:00
)
. map_err ( | err | format! ( " Validation failed: {} " , err ) ) ? ;
2019-04-03 16:52:37 -07:00
Ok ( ( ) )
}
/// Runs logic for the `validate` subcommand
fn validate ( validate : Validate ) {
match validate_wasm ( validate ) {
Err ( message ) = > {
eprintln! ( " Error: {} " , message ) ;
exit ( - 1 ) ;
}
_ = > ( ) ,
}
}
2019-11-20 01:52:33 +08:00
fn get_compiler_by_backend ( backend : Backend , _opts : & Run ) -> Option < Box < dyn Compiler > > {
2019-07-30 22:25:58 +08:00
Some ( match backend {
#[ cfg(feature = " backend-singlepass " ) ]
2019-10-11 21:05:59 +08:00
Backend ::Singlepass = > {
2019-11-20 01:56:13 +08:00
use wasmer_runtime_core ::codegen ::MiddlewareChain ;
2019-10-13 20:02:47 +08:00
use wasmer_runtime_core ::codegen ::StreamingCompiler ;
2019-10-11 21:05:59 +08:00
use wasmer_singlepass_backend ::ModuleCodeGenerator as SinglePassMCG ;
2019-11-20 01:52:33 +08:00
let opts = _opts . clone ( ) ;
let middlewares_gen = move | | {
let mut middlewares = MiddlewareChain ::new ( ) ;
if opts . call_trace {
use wasmer_middleware_common ::call_trace ::CallTrace ;
middlewares . push ( CallTrace ::new ( ) ) ;
}
if opts . block_trace {
use wasmer_middleware_common ::block_trace ::BlockTrace ;
middlewares . push ( BlockTrace ::new ( ) ) ;
}
middlewares
} ;
2019-10-11 21:05:59 +08:00
2019-10-13 20:02:47 +08:00
let c : StreamingCompiler < SinglePassMCG , _ , _ , _ , _ > =
StreamingCompiler ::new ( middlewares_gen ) ;
2019-10-11 21:05:59 +08:00
Box ::new ( c )
}
2019-07-30 22:25:58 +08:00
#[ cfg(not(feature = " backend-singlepass " )) ]
Backend ::Singlepass = > return None ,
2019-11-21 23:31:10 +08:00
#[ cfg(feature = " backend-cranelift " ) ]
2019-07-30 22:25:58 +08:00
Backend ::Cranelift = > Box ::new ( CraneliftCompiler ::new ( ) ) ,
2019-11-21 23:37:17 +08:00
#[ cfg(not(feature = " backend-cranelift " )) ]
Backend ::Cranelift = > return None ,
2019-07-30 22:25:58 +08:00
#[ cfg(feature = " backend-llvm " ) ]
Backend ::LLVM = > Box ::new ( LLVMCompiler ::new ( ) ) ,
#[ cfg(not(feature = " backend-llvm " )) ]
Backend ::LLVM = > return None ,
2019-11-22 19:02:57 +09:00
Backend ::Auto = > return None ,
2019-07-30 22:25:58 +08:00
} )
}
2018-10-14 23:47:35 +02:00
fn main ( ) {
2019-11-20 17:15:20 -08:00
// We try to run wasmer with the normal arguments.
// Eg. `wasmer <SUBCOMMAND>`
// In case that fails, we fallback trying the Run subcommand directly.
// Eg. `wasmer myfile.wasm --dir=.`
2019-11-20 17:19:42 -08:00
let options = CLIOptions ::from_iter_safe ( env ::args ( ) ) . unwrap_or_else ( | e | {
match e . kind {
// This fixes a issue that:
// 1. Shows the version twice when doing `wasmer -V`
// 2. Shows the run help (instead of normal help) when doing `wasmer --help`
clap ::ErrorKind ::VersionDisplayed | clap ::ErrorKind ::HelpDisplayed = > e . exit ( ) ,
_ = > CLIOptions ::Run ( Run ::from_args ( ) ) ,
}
} ) ;
2018-10-14 23:47:35 +02:00
match options {
2019-11-22 19:02:57 +09:00
CLIOptions ::Run ( mut options ) = > run ( & mut options ) ,
2019-02-14 15:30:42 -08:00
#[ cfg(not(target_os = " windows " )) ]
2018-11-25 21:31:32 -08:00
CLIOptions ::SelfUpdate = > update ::self_update ( ) ,
2019-02-14 15:30:42 -08:00
#[ cfg(target_os = " windows " ) ]
CLIOptions ::SelfUpdate = > {
println! ( " Self update is not supported on Windows. Use install instructions on the Wasmer homepage: https://wasmer.io " ) ;
}
2019-02-22 11:42:30 -08:00
CLIOptions ::Cache ( cache ) = > match cache {
Cache ::Clean = > {
2019-02-25 11:46:48 -08:00
use std ::fs ;
2019-02-22 11:42:30 -08:00
let cache_dir = get_cache_dir ( ) ;
2019-03-19 11:08:14 -07:00
if cache_dir . exists ( ) {
fs ::remove_dir_all ( cache_dir . clone ( ) ) . expect ( " Can't remove cache dir " ) ;
}
fs ::create_dir_all ( cache_dir . clone ( ) ) . expect ( " Can't create cache dir " ) ;
2019-02-22 11:42:30 -08:00
}
Cache ::Dir = > {
println! ( " {} " , get_cache_dir ( ) . to_string_lossy ( ) ) ;
}
} ,
2019-04-03 16:52:37 -07:00
CLIOptions ::Validate ( validate_options ) = > {
validate ( validate_options ) ;
}
2018-10-14 23:47:35 +02:00
}
}
2019-07-29 18:25:12 -07:00
#[ test ]
fn filesystem_cache_should_work ( ) -> Result < ( ) , String > {
let wasmer_cache_dir = get_cache_dir ( ) ;
2019-07-29 18:29:20 -07:00
unsafe { FileSystemCache ::new ( wasmer_cache_dir ) . map_err ( | e | format! ( " Cache error: {:?} " , e ) ) ? } ;
2019-07-29 18:25:12 -07:00
Ok ( ( ) )
}