diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e2a4b2c788..e9eae516e46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Looking for changes that affect our C API? See the [C API Changelog](lib/c-api/C ### Changed - [#3003](https://github.com/wasmerio/wasmer/pull/3003) Remove RuntimeError::raise from public API +- [#2999](https://github.com/wasmerio/wasmer/pull/2999) Allow `--invoke` CLI option for Emscripten files without a `main` function - [#2946](https://github.com/wasmerio/wasmer/pull/2946) Remove dylib,staticlib engines in favor of a single Universal engine - [#2949](https://github.com/wasmerio/wasmer/pull/2949) Switch back to using custom LLVM builds on CI diff --git a/Makefile b/Makefile index 93fa0ca5749..66d618fd0f3 100644 --- a/Makefile +++ b/Makefile @@ -539,7 +539,7 @@ test-examples: $(CARGO_BINARY) test $(CARGO_TARGET) --release $(compiler_features) --features wasi --examples test-integration: - $(CARGO_BINARY) test $(CARGO_TARGET) -p wasmer-integration-tests-cli + $(CARGO_BINARY) test $(CARGO_TARGET) --no-fail-fast -p wasmer-integration-tests-cli test-integration-ios: $(CARGO_BINARY) test $(CARGO_TARGET) -p wasmer-integration-tests-ios diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index ec7b81a2cfc..ea5e38f415d 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -105,9 +105,6 @@ impl Run { }; // TODO: refactor this if is_emscripten_module(&module) { - if self.invoke.is_some() { - bail!("--invoke is not supported with emscripten modules"); - } let mut emscripten_globals = EmscriptenGlobals::new(module.store(), &module) .map_err(|e| anyhow!("{}", e))?; let mut em_env = EmEnv::new(&emscripten_globals.data, Default::default()); @@ -137,7 +134,7 @@ impl Run { self.path.to_str().unwrap() }, self.args.iter().map(|arg| arg.as_str()).collect(), - None, //run.em_entrypoint.clone(), + self.invoke.clone(), )?; return Ok(()); } diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs index 30e619a69c5..294d0c0af22 100644 --- a/lib/emscripten/src/lib.rs +++ b/lib/emscripten/src/lib.rs @@ -23,9 +23,9 @@ use std::f64; use std::path::PathBuf; use std::sync::{Arc, Mutex, RwLock}; use wasmer::{ - imports, namespace, Exports, Function, FunctionType, Global, Imports, Instance, LazyInit, - Memory, MemoryType, Module, Pages, RuntimeError, Store, Table, TableType, TypedFunction, Val, - ValType, WasmPtr, WasmerEnv, + imports, namespace, ExportError, Exports, Function, FunctionType, Global, Imports, Instance, + LazyInit, Memory, MemoryType, Module, Pages, RuntimeError, Store, Table, TableType, + TypedFunction, Val, ValType, WasmPtr, WasmerEnv, }; #[cfg(unix)] @@ -313,24 +313,51 @@ pub fn set_up_emscripten(instance: &mut Instance) -> Result<(), RuntimeError> { Ok(()) } +/// Looks for variations of the main function (usually +/// `["_main", "main"])`, then returns a reference to +/// the name of the first found function. Useful for +/// determining whether a module is executable. +/// +/// Returns `ExportError` if none of the `main_func_names` +/// were found. +pub fn emscripten_get_main_func_name<'a>( + instance: &Instance, + main_func_names: &[&'a str], +) -> Result<&'a str, ExportError> { + let mut last_err = None; + + for func_name in main_func_names.iter() { + match instance.exports.get::(func_name) { + Ok(_) => { + return Ok(func_name); + } + Err(e) => { + last_err = Some(e); + } + } + } + + match last_err { + None => Err(ExportError::Missing(format!("{main_func_names:?}"))), + Some(e) => Err(e), + } +} + /// Call the main function in emscripten, assumes that the emscripten state is /// set up. /// /// If you don't want to set it up yourself, consider using [`run_emscripten_instance`]. pub fn emscripten_call_main( instance: &mut Instance, + function_name: &str, env: &EmEnv, path: &str, args: &[&str], ) -> Result<(), RuntimeError> { - let (function_name, main_func) = match instance.exports.get::("_main") { - Ok(func) => Ok(("_main", func)), - Err(_e) => instance - .exports - .get::("main") - .map(|func| ("main", func)), - } - .map_err(|e| RuntimeError::new(e.to_string()))?; + let main_func = instance + .exports + .get::(function_name) + .map_err(|e| RuntimeError::new(e.to_string()))?; let num_params = main_func.ty().params().len(); let _result = match num_params { 2 => { @@ -373,19 +400,16 @@ pub fn run_emscripten_instance( env.set_memory(globals.memory.clone()); set_up_emscripten(instance)?; - // println!("running emscripten instance"); - - if let Some(ep) = entrypoint { - debug!("Running entry point: {}", &ep); - let arg = unsafe { allocate_cstr_on_stack(env, args[0]).0 }; - //let (argc, argv) = store_module_arguments(instance.context_mut(), args); - let func: &Function = instance - .exports - .get(&ep) - .map_err(|e| RuntimeError::new(e.to_string()))?; - func.call(&[Val::I32(arg as i32)])?; + let main_func_names = ["_main", "main"]; + if let Some(ep) = entrypoint.as_ref() { + debug!("Running entry point: {}", ep); + emscripten_call_main(instance, ep, env, path, &args)?; + } else if let Ok(name) = emscripten_get_main_func_name(instance, &main_func_names) { + emscripten_call_main(instance, name, env, path, &args)?; } else { - emscripten_call_main(instance, env, path, &args)?; + return Err(RuntimeError::new(format!( + "No main function found (searched: {main_func_names:?}) and no entrypoint specified" + ))); } // TODO atexit for emscripten