From f1c95a81ab4e574c45dfa8b1e38b0141434e87ee Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 13 Dec 2019 13:04:54 -0800 Subject: [PATCH 1/5] Add foundational emscripten functions and types to C API --- Cargo.lock | 1 + lib/runtime-c-api/Cargo.toml | 6 +++ lib/runtime-c-api/build.rs | 21 ++++++-- lib/runtime-c-api/src/import/emscripten.rs | 58 ++++++++++++++++++++++ lib/runtime-c-api/src/import/mod.rs | 9 ++++ lib/runtime-c-api/wasmer.h | 41 +++++++++++++-- lib/runtime-c-api/wasmer.hh | 33 ++++++++++-- 7 files changed, 156 insertions(+), 13 deletions(-) create mode 100644 lib/runtime-c-api/src/import/emscripten.rs diff --git a/Cargo.lock b/Cargo.lock index 0ee127d57ad..c424cd73114 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2286,6 +2286,7 @@ version = "0.11.0" dependencies = [ "cbindgen 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-emscripten 0.11.0", "wasmer-runtime 0.11.0", "wasmer-runtime-core 0.11.0", "wasmer-wasi 0.11.0", diff --git a/lib/runtime-c-api/Cargo.toml b/lib/runtime-c-api/Cargo.toml index 0a278b6ba2e..1b404eae6e9 100644 --- a/lib/runtime-c-api/Cargo.toml +++ b/lib/runtime-c-api/Cargo.toml @@ -32,6 +32,11 @@ path = "../wasi" version = "0.11.0" optional = true +[dependencies.wasmer-emscripten] +path = "../emscripten" +version = "0.11.0" +optional = true + [features] default = ["cranelift-backend", "wasi"] debug = ["wasmer-runtime/debug"] @@ -39,6 +44,7 @@ cranelift-backend = ["wasmer-runtime/cranelift", "wasmer-runtime/default-backend llvm-backend = ["wasmer-runtime/llvm", "wasmer-runtime/default-backend-llvm"] singlepass-backend = ["wasmer-runtime/singlepass", "wasmer-runtime/default-backend-singlepass"] wasi = ["wasmer-wasi"] +emscripten = ["wasmer-emscripten"] [build-dependencies] cbindgen = "0.9" diff --git a/lib/runtime-c-api/build.rs b/lib/runtime-c-api/build.rs index 5897001b043..b20a9acd7ec 100644 --- a/lib/runtime-c-api/build.rs +++ b/lib/runtime-c-api/build.rs @@ -12,7 +12,7 @@ fn main() { let mut out_wasmer_header_file = PathBuf::from(&out_dir); out_wasmer_header_file.push("wasmer"); - const WASMER_PRE_HEADER: &str = r#" + let mut pre_header = r#" #if !defined(WASMER_H_MACROS) #define WASMER_H_MACROS @@ -28,17 +28,27 @@ fn main() { #endif #endif -#endif // WASMER_H_MACROS -"#; +"# + .to_string(); + + #[cfg(feature = "emscripten")] + { + pre_header += "#define WASMER_EMSCRIPTEN_ENABLED\n"; + } + + // close pre header + pre_header += "#endif // WASMER_H_MACROS\n"; + // Generate the C bindings in the `OUT_DIR`. out_wasmer_header_file.set_extension("h"); Builder::new() .with_crate(crate_dir.clone()) .with_language(Language::C) .with_include_guard("WASMER_H") - .with_header(WASMER_PRE_HEADER) + .with_header(&pre_header) .with_define("target_family", "windows", "_WIN32") .with_define("target_arch", "x86_64", "ARCH_X86_64") + .with_define("feature", "emscripten", "WASMER_EMSCRIPTEN_ENABLED") .generate() .expect("Unable to generate C bindings") .write_to_file(out_wasmer_header_file.as_path()); @@ -49,9 +59,10 @@ fn main() { .with_crate(crate_dir) .with_language(Language::Cxx) .with_include_guard("WASMER_H") - .with_header(WASMER_PRE_HEADER) + .with_header(&pre_header) .with_define("target_family", "windows", "_WIN32") .with_define("target_arch", "x86_64", "ARCH_X86_64") + .with_define("feature", "emscripten", "WASMER_EMSCRIPTEN_ENABLED") .generate() .expect("Unable to generate C++ bindings") .write_to_file(out_wasmer_header_file.as_path()); diff --git a/lib/runtime-c-api/src/import/emscripten.rs b/lib/runtime-c-api/src/import/emscripten.rs new file mode 100644 index 00000000000..caced6a620c --- /dev/null +++ b/lib/runtime-c-api/src/import/emscripten.rs @@ -0,0 +1,58 @@ +//! Functions and types for dealing with Emscripten imports + +use super::*; +use crate::module::wasmer_module_t; +use std::ptr; +use wasmer_emscripten::EmscriptenGlobals; +use wasmer_runtime::Module; + +/// Type used to construct an import_object_t with Emscripten imports. +#[repr(C)] +pub struct wasmer_emscripten_globals_t; + +/// Create a `wasmer_emscripten_globals_t` from a Wasm module. +#[no_mangle] +pub unsafe extern "C" fn wasmer_emscripten_get_emscripten_globals( + module: *const wasmer_module_t, +) -> *mut wasmer_emscripten_globals_t { + if module.is_null() { + return ptr::null_mut(); + } + let module = &*(module as *const Module); + match EmscriptenGlobals::new(module) { + Ok(globals) => Box::into_raw(Box::new(globals)) as *mut wasmer_emscripten_globals_t, + Err(msg) => { + update_last_error(CApiError { msg }); + return ptr::null_mut(); + } + } +} + +/// Destroy `wasmer_emscrpten_globals_t` created by +/// `wasmer_emscripten_get_emscripten_globals`. +#[no_mangle] +pub unsafe extern "C" fn wasmer_emscripten_destroy_emscripten_globals( + globals: *mut wasmer_emscripten_globals_t, +) { + if globals.is_null() { + return; + } + let _ = Box::from_raw(globals); +} + +/// Create a `wasmer_import_object_t` with Emscripten imports, use +/// `wasmer_emscripten_get_emscripten_globals` to get a +/// `wasmer_emscripten_globals_t` from a `wasmer_module_t`. +#[no_mangle] +pub unsafe extern "C" fn wasmer_emscripten_generate_import_object( + globals: *mut wasmer_emscripten_globals_t, +) -> *mut wasmer_import_object_t { + if globals.is_null() { + return ptr::null_mut(); + } + // TODO: figure out if we should be using UnsafeCell here or something + let g = &mut *(globals as *mut EmscriptenGlobals); + let import_object = Box::new(wasmer_emscripten::generate_emscripten_env(g)); + + Box::into_raw(import_object) as *mut wasmer_import_object_t +} diff --git a/lib/runtime-c-api/src/import/mod.rs b/lib/runtime-c-api/src/import/mod.rs index 2bca1e3902c..97886211387 100644 --- a/lib/runtime-c-api/src/import/mod.rs +++ b/lib/runtime-c-api/src/import/mod.rs @@ -61,6 +61,12 @@ mod wasi; #[cfg(feature = "wasi")] pub use self::wasi::*; +#[cfg(feature = "emscripten")] +mod emscripten; + +#[cfg(feature = "emscripten")] +pub use self::emscripten::*; + /// Gets an entry from an ImportObject at the name and namespace. /// Stores `name`, `namespace`, and `import_export_value` in `import`. /// Thus these must remain valid for the lifetime of `import`. @@ -437,6 +443,9 @@ pub unsafe extern "C" fn wasmer_import_descriptors( module: *const wasmer_module_t, import_descriptors: *mut *mut wasmer_import_descriptors_t, ) { + if module.is_null() { + return; + } let module = &*(module as *const Module); let total_imports = module.info().imported_functions.len() + module.info().imported_tables.len() diff --git a/lib/runtime-c-api/wasmer.h b/lib/runtime-c-api/wasmer.h index de921e3b04d..d313f61a9e7 100644 --- a/lib/runtime-c-api/wasmer.h +++ b/lib/runtime-c-api/wasmer.h @@ -74,6 +74,19 @@ typedef struct { } wasmer_module_t; +#if defined(WASMER_EMSCRIPTEN_ENABLED) +/** + * Type used to construct an import_object_t with Emscripten imports. + */ +typedef struct { + +} wasmer_emscripten_globals_t; +#endif + +typedef struct { + +} wasmer_import_object_t; + /** * Opaque pointer to `NamedExportDescriptor`. */ @@ -153,10 +166,6 @@ typedef struct { typedef struct { -} wasmer_import_object_t; - -typedef struct { - } wasmer_table_t; /** @@ -247,6 +256,30 @@ wasmer_result_t wasmer_compile(wasmer_module_t **module, uint8_t *wasm_bytes, uint32_t wasm_bytes_len); +#if defined(WASMER_EMSCRIPTEN_ENABLED) +/** + * Destroy `wasmer_emscrpten_globals_t` created by + * `wasmer_emscripten_get_emscripten_globals`. + */ +void wasmer_emscripten_destroy_emscripten_globals(wasmer_emscripten_globals_t *globals); +#endif + +#if defined(WASMER_EMSCRIPTEN_ENABLED) +/** + * Create a `wasmer_import_object_t` with Emscripten imports, use + * `wasmer_emscripten_get_emscripten_globals` to get a + * `wasmer_emscripten_globals_t` from a `wasmer_module_t`. + */ +wasmer_import_object_t *wasmer_emscripten_generate_import_object(wasmer_emscripten_globals_t *globals); +#endif + +#if defined(WASMER_EMSCRIPTEN_ENABLED) +/** + * Create a `wasmer_emscripten_globals_t` from a Wasm module. + */ +wasmer_emscripten_globals_t *wasmer_emscripten_get_emscripten_globals(const wasmer_module_t *module); +#endif + /** * Gets export descriptor kind */ diff --git a/lib/runtime-c-api/wasmer.hh b/lib/runtime-c-api/wasmer.hh index 09b58f86100..a81b8c77ac5 100644 --- a/lib/runtime-c-api/wasmer.hh +++ b/lib/runtime-c-api/wasmer.hh @@ -61,6 +61,17 @@ struct wasmer_module_t { }; +#if defined(WASMER_EMSCRIPTEN_ENABLED) +/// Type used to construct an import_object_t with Emscripten imports. +struct wasmer_emscripten_globals_t { + +}; +#endif + +struct wasmer_import_object_t { + +}; + /// Opaque pointer to `NamedExportDescriptor`. struct wasmer_export_descriptor_t { @@ -128,10 +139,6 @@ struct wasmer_import_func_t { }; -struct wasmer_import_object_t { - -}; - struct wasmer_table_t { }; @@ -216,6 +223,24 @@ wasmer_result_t wasmer_compile(wasmer_module_t **module, uint8_t *wasm_bytes, uint32_t wasm_bytes_len); +#if defined(WASMER_EMSCRIPTEN_ENABLED) +/// Destroy `wasmer_emscrpten_globals_t` created by +/// `wasmer_emscripten_get_emscripten_globals`. +void wasmer_emscripten_destroy_emscripten_globals(wasmer_emscripten_globals_t *globals); +#endif + +#if defined(WASMER_EMSCRIPTEN_ENABLED) +/// Create a `wasmer_import_object_t` with Emscripten imports, use +/// `wasmer_emscripten_get_emscripten_globals` to get a +/// `wasmer_emscripten_globals_t` from a `wasmer_module_t`. +wasmer_import_object_t *wasmer_emscripten_generate_import_object(wasmer_emscripten_globals_t *globals); +#endif + +#if defined(WASMER_EMSCRIPTEN_ENABLED) +/// Create a `wasmer_emscripten_globals_t` from a Wasm module. +wasmer_emscripten_globals_t *wasmer_emscripten_get_emscripten_globals(const wasmer_module_t *module); +#endif + /// Gets export descriptor kind wasmer_import_export_kind wasmer_export_descriptor_kind(wasmer_export_descriptor_t *export_); From e86b3941d02a26d9ed9541f966dddbbb0891ac1f Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 16 Dec 2019 15:45:28 -0500 Subject: [PATCH 2/5] Update Emscripten API layout; expose inner parts to to C API; add test --- lib/emscripten/src/lib.rs | 93 ++++--- lib/runtime-c-api/src/import/emscripten.rs | 84 ++++++- lib/runtime-c-api/tests/.gitignore | 3 +- lib/runtime-c-api/tests/CMakeLists.txt | 11 + .../tests/assets/emscripten_hello_world.c | 10 + .../tests/assets/emscripten_hello_world.wasm | Bin 0 -> 10435 bytes .../tests/runtime_c_api_tests.rs | 4 +- .../tests/test-emscripten-import-object.c | 235 ++++++++++++++++++ lib/runtime-c-api/wasmer.h | 53 +++- lib/runtime-c-api/wasmer.hh | 49 +++- 10 files changed, 484 insertions(+), 58 deletions(-) create mode 100644 lib/runtime-c-api/tests/assets/emscripten_hello_world.c create mode 100644 lib/runtime-c-api/tests/assets/emscripten_hello_world.wasm create mode 100644 lib/runtime-c-api/tests/test-emscripten-import-object.c diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs index 1c9b8a1f70d..d3dff3169ae 100644 --- a/lib/emscripten/src/lib.rs +++ b/lib/emscripten/src/lib.rs @@ -325,6 +325,61 @@ impl<'a> EmscriptenData<'a> { } } +/// Call the global constructors for C++ and set up the emscripten environment. +/// +/// Note that this function does not completely set up Emscripten to be called. +/// before calling this function, please initialize `Ctx::data` with a pointer +/// to [`EmscriptenData`]. +pub fn set_up_emscripten(instance: &mut Instance) -> CallResult<()> { + // ATINIT + // (used by C++) + if let Ok(_func) = instance.dyn_func("globalCtors") { + instance.call("globalCtors", &[])?; + } + + if let Ok(_func) = instance.dyn_func("___emscripten_environ_constructor") { + instance.call("___emscripten_environ_constructor", &[])?; + } + Ok(()) +} + +/// 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, path: &str, args: &[&str]) -> CallResult<()> { + let (func_name, main_func) = match instance.dyn_func("_main") { + Ok(func) => Ok(("_main", func)), + Err(_e) => match instance.dyn_func("main") { + Ok(func) => Ok(("main", func)), + Err(e) => Err(e), + }, + }?; + let num_params = main_func.signature().params().len(); + let _result = match num_params { + 2 => { + let mut new_args = vec![path]; + new_args.extend(args); + let (argc, argv) = store_module_arguments(instance.context_mut(), new_args); + instance.call( + func_name, + &[Value::I32(argc as i32), Value::I32(argv as i32)], + )?; + } + 0 => { + instance.call(func_name, &[])?; + } + _ => { + return Err(CallError::Resolve(ResolveError::ExportWrongType { + name: "main".to_string(), + })) + } + }; + + Ok(()) +} + +/// Top level function to execute emscripten pub fn run_emscripten_instance( _module: &Module, instance: &mut Instance, @@ -338,15 +393,7 @@ pub fn run_emscripten_instance( let data_ptr = &mut data as *mut _ as *mut c_void; instance.context_mut().data = data_ptr; - // ATINIT - // (used by C++) - if let Ok(_func) = instance.dyn_func("globalCtors") { - instance.call("globalCtors", &[])?; - } - - if let Ok(_func) = instance.dyn_func("___emscripten_environ_constructor") { - instance.call("___emscripten_environ_constructor", &[])?; - } + set_up_emscripten(instance)?; // println!("running emscripten instance"); @@ -356,33 +403,7 @@ pub fn run_emscripten_instance( //let (argc, argv) = store_module_arguments(instance.context_mut(), args); instance.call(&ep, &[Value::I32(arg as i32)])?; } else { - let (func_name, main_func) = match instance.dyn_func("_main") { - Ok(func) => Ok(("_main", func)), - Err(_e) => match instance.dyn_func("main") { - Ok(func) => Ok(("main", func)), - Err(e) => Err(e), - }, - }?; - let num_params = main_func.signature().params().len(); - let _result = match num_params { - 2 => { - let mut new_args = vec![path]; - new_args.extend(args); - let (argc, argv) = store_module_arguments(instance.context_mut(), new_args); - instance.call( - func_name, - &[Value::I32(argc as i32), Value::I32(argv as i32)], - )?; - } - 0 => { - instance.call(func_name, &[])?; - } - _ => { - return Err(CallError::Resolve(ResolveError::ExportWrongType { - name: "main".to_string(), - })) - } - }; + emscripten_call_main(instance, path, &args)?; } // TODO atexit for emscripten diff --git a/lib/runtime-c-api/src/import/emscripten.rs b/lib/runtime-c-api/src/import/emscripten.rs index caced6a620c..7a9b111ddb0 100644 --- a/lib/runtime-c-api/src/import/emscripten.rs +++ b/lib/runtime-c-api/src/import/emscripten.rs @@ -1,10 +1,11 @@ //! Functions and types for dealing with Emscripten imports use super::*; -use crate::module::wasmer_module_t; +use crate::{get_slice_checked, instance::wasmer_instance_t, module::wasmer_module_t}; + use std::ptr; use wasmer_emscripten::EmscriptenGlobals; -use wasmer_runtime::Module; +use wasmer_runtime::{Instance, Module}; /// Type used to construct an import_object_t with Emscripten imports. #[repr(C)] @@ -40,9 +41,88 @@ pub unsafe extern "C" fn wasmer_emscripten_destroy_emscripten_globals( let _ = Box::from_raw(globals); } +/// Execute global constructors (required if the module is compiled from C++) +/// and sets up the internal environment. +/// +/// This function sets the data pointer in the same way that +/// [`wasmer_instance_context_data_set`] does. +#[no_mangle] +pub unsafe extern "C" fn wasmer_emscripten_set_up_emscripten( + instance: *mut wasmer_instance_t, + globals: *mut wasmer_emscripten_globals_t, +) -> wasmer_result_t { + if globals.is_null() || instance.is_null() { + return wasmer_result_t::WASMER_ERROR; + } + let instance = &mut *(instance as *mut Instance); + instance.context_mut().data = globals as *mut c_void; + match wasmer_emscripten::set_up_emscripten(instance) { + Ok(_) => wasmer_result_t::WASMER_OK, + Err(e) => { + update_last_error(e); + wasmer_result_t::WASMER_ERROR + } + } +} + +/// Convenience function for setting up arguments and calling the Emscripten +/// main function. +/// +/// WARNING: +/// +/// Do not call this function on untrusted code when operating without +/// additional sandboxing in place. +/// Emscripten has access to many host system calls and therefore may do very +/// bad things. +#[no_mangle] +pub unsafe extern "C" fn wasmer_emscripten_call_main( + instance: *mut wasmer_instance_t, + args: *const wasmer_byte_array, + args_len: c_uint, +) -> wasmer_result_t { + if instance.is_null() || args.is_null() { + return wasmer_result_t::WASMER_ERROR; + } + let instance = &mut *(instance as *mut Instance); + + let arg_list = get_slice_checked(args, args_len as usize); + let arg_process_result: Result, _> = + arg_list.iter().map(|arg| arg.as_str()).collect(); + let arg_vec = match arg_process_result.as_ref() { + Ok(arg_vec) => arg_vec, + Err(err) => { + update_last_error(*err); + return wasmer_result_t::WASMER_ERROR; + } + }; + + let prog_name = if let Some(prog_name) = arg_vec.first() { + prog_name + } else { + update_last_error(CApiError { + msg: "First argument (program name) is required to execute Emscripten's main function" + .to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + }; + + match wasmer_emscripten::emscripten_call_main(instance, prog_name, &arg_vec[1..]) { + Ok(_) => wasmer_result_t::WASMER_OK, + Err(e) => { + update_last_error(e); + wasmer_result_t::WASMER_ERROR + } + } +} + /// Create a `wasmer_import_object_t` with Emscripten imports, use /// `wasmer_emscripten_get_emscripten_globals` to get a /// `wasmer_emscripten_globals_t` from a `wasmer_module_t`. +/// +/// WARNING: +///1 +/// This `import_object_t` contains thin-wrappers around host system calls. +/// Do not use this to execute untrusted code without additional sandboxing. #[no_mangle] pub unsafe extern "C" fn wasmer_emscripten_generate_import_object( globals: *mut wasmer_emscripten_globals_t, diff --git a/lib/runtime-c-api/tests/.gitignore b/lib/runtime-c-api/tests/.gitignore index cbc7f3b1fcf..b62699da34d 100644 --- a/lib/runtime-c-api/tests/.gitignore +++ b/lib/runtime-c-api/tests/.gitignore @@ -26,4 +26,5 @@ test-module-imports test-module-serialize test-tables test-validate -test-wasi-import-object \ No newline at end of file +test-wasi-import-object +test-emscripten-import-object \ No newline at end of file diff --git a/lib/runtime-c-api/tests/CMakeLists.txt b/lib/runtime-c-api/tests/CMakeLists.txt index 20f7dea13bd..f9aecd4d694 100644 --- a/lib/runtime-c-api/tests/CMakeLists.txt +++ b/lib/runtime-c-api/tests/CMakeLists.txt @@ -22,6 +22,11 @@ if (DEFINED WASI_TESTS) add_executable(test-wasi-import-object test-wasi-import-object.c) endif() +if (DEFINED EMSCRIPTEN_TESTS) + add_executable(test-emscripten-import-object test-emscripten-import-object.c) +endif() + + find_library( WASMER_LIB NAMES libwasmer_runtime_c_api.dylib libwasmer_runtime_c_api.so wasmer_runtime_c_api.dll PATHS ${CMAKE_SOURCE_DIR}/../../../target/release/ @@ -74,6 +79,12 @@ if (DEFINED WASI_TESTS) add_test(test-wasi-import-object test-wasi-import-object) endif() +if (DEFINED EMSCRIPTEN_TESTS) + target_link_libraries(test-emscripten-import-object general ${WASMER_LIB}) + target_compile_options(test-emscripten-import-object PRIVATE ${COMPILER_OPTIONS}) + add_test(test-emscripten-import-object test-emscripten-import-object) +endif() + target_link_libraries(test-instantiate general ${WASMER_LIB}) target_compile_options(test-instantiate PRIVATE ${COMPILER_OPTIONS}) add_test(test-instantiate test-instantiate) diff --git a/lib/runtime-c-api/tests/assets/emscripten_hello_world.c b/lib/runtime-c-api/tests/assets/emscripten_hello_world.c new file mode 100644 index 00000000000..998892f20c9 --- /dev/null +++ b/lib/runtime-c-api/tests/assets/emscripten_hello_world.c @@ -0,0 +1,10 @@ +#include + +int main(int argc, char *argv[]) { + printf("Hello, world\n"); + for ( int i = 0; i < argc; ++i ) { + printf("Arg %d: '%s'\n", i, argv[i]); + } + return 0; +} + diff --git a/lib/runtime-c-api/tests/assets/emscripten_hello_world.wasm b/lib/runtime-c-api/tests/assets/emscripten_hello_world.wasm new file mode 100644 index 0000000000000000000000000000000000000000..96100d4e45cd45772bd9124fa732a9902f47b5df GIT binary patch literal 10435 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 U{!Mf8@pW_g+-384c--9oe`(s>{{R30 literal 0 HcmV?d00001 diff --git a/lib/runtime-c-api/tests/runtime_c_api_tests.rs b/lib/runtime-c-api/tests/runtime_c_api_tests.rs index ed414636374..a5370608274 100644 --- a/lib/runtime-c-api/tests/runtime_c_api_tests.rs +++ b/lib/runtime-c-api/tests/runtime_c_api_tests.rs @@ -8,8 +8,10 @@ fn test_c_api() { ".", #[cfg(feature = "wasi")] "-DWASI_TESTS=ON", + #[cfg(feature = "emscripten")] + "-DEMSCRIPTEN_TESTS=ON", ]; - // we use -f so it doesn't fail if the fiel doesn't exist + // we use -f so it doesn't fail if the file doesn't exist run_command("rm", project_tests_dir, vec!["-f", "CMakeCache.txt"]); run_command("cmake", project_tests_dir, cmake_args); run_command("make", project_tests_dir, vec!["-Wdev", "-Werror=dev"]); diff --git a/lib/runtime-c-api/tests/test-emscripten-import-object.c b/lib/runtime-c-api/tests/test-emscripten-import-object.c new file mode 100644 index 00000000000..03b196f59a8 --- /dev/null +++ b/lib/runtime-c-api/tests/test-emscripten-import-object.c @@ -0,0 +1,235 @@ +#include +#include "../wasmer.h" +#include +#include +#include + +static bool host_print_called = false; + +// Host function that will be imported into the Web Assembly Instance +void host_print(const wasmer_instance_context_t *ctx, int32_t ptr, int32_t len) +{ + host_print_called = true; + const wasmer_memory_t *memory = wasmer_instance_context_memory(ctx, 0); + uint32_t mem_len = wasmer_memory_length(memory); + uint8_t *mem_bytes = wasmer_memory_data(memory); + printf("%.*s", len, mem_bytes + ptr); +} + +// Use the last_error API to retrieve error messages +void print_wasmer_error() +{ + int error_len = wasmer_last_error_length(); + printf("Error len: `%d`\n", error_len); + char *error_str = malloc(error_len); + wasmer_last_error_message(error_str, error_len); + printf("Error str: `%s`\n", error_str); +} + +// helper function to print byte array to stdout +void print_byte_array(wasmer_byte_array *arr) { + for (int i = 0; i < arr->bytes_len; ++i) { + putchar(arr->bytes[i]); + } +} + +int main() +{ + // Create a new func to hold the parameter and signature + // of our `host_print` host function + wasmer_value_tag params_sig[] = {WASM_I32, WASM_I32}; + wasmer_value_tag returns_sig[] = {}; + wasmer_import_func_t *func = wasmer_import_func_new((void (*)(void *)) host_print, params_sig, 2, returns_sig, 0); + + // Create module name for our imports + // represented in bytes for UTF-8 compatability + const char *module_name = "env"; + wasmer_byte_array module_name_bytes; + module_name_bytes.bytes = (const uint8_t *) module_name; + module_name_bytes.bytes_len = strlen(module_name); + + // Define a function import + const char *import_name = "host_print"; + wasmer_byte_array import_name_bytes; + import_name_bytes.bytes = (const uint8_t *) import_name; + import_name_bytes.bytes_len = strlen(import_name); + wasmer_import_t func_import; + func_import.module_name = module_name_bytes; + func_import.import_name = import_name_bytes; + func_import.tag = WASM_FUNCTION; + func_import.value.func = func; + + // Define a memory import + const char *import_memory_name = "memory"; + wasmer_byte_array import_memory_name_bytes; + import_memory_name_bytes.bytes = (const uint8_t *) import_memory_name; + import_memory_name_bytes.bytes_len = strlen(import_memory_name); + wasmer_import_t memory_import; + memory_import.module_name = module_name_bytes; + memory_import.import_name = import_memory_name_bytes; + memory_import.tag = WASM_MEMORY; + wasmer_memory_t *memory = NULL; + wasmer_limits_t descriptor; + descriptor.min = 256; + wasmer_limit_option_t max; + max.has_some = true; + max.some = 256; + descriptor.max = max; + wasmer_result_t memory_result = wasmer_memory_new(&memory, descriptor); + if (memory_result != WASMER_OK) + { + print_wasmer_error(); + } + memory_import.value.memory = memory; + + // Define a global import + const char *import_global_name = "__memory_base"; + wasmer_byte_array import_global_name_bytes; + import_global_name_bytes.bytes = (const uint8_t *) import_global_name; + import_global_name_bytes.bytes_len = strlen(import_global_name); + wasmer_import_t global_import; + global_import.module_name = module_name_bytes; + global_import.import_name = import_global_name_bytes; + global_import.tag = WASM_GLOBAL; + wasmer_value_t val; + val.tag = WASM_I32; + val.value.I32 = 1024; + wasmer_global_t *global = wasmer_global_new(val, false); + global_import.value.global = global; + + // Define a table import + const char *import_table_name = "table"; + wasmer_byte_array import_table_name_bytes; + import_table_name_bytes.bytes = (const uint8_t *) import_table_name; + import_table_name_bytes.bytes_len = strlen(import_table_name); + wasmer_import_t table_import; + table_import.module_name = module_name_bytes; + table_import.import_name = import_table_name_bytes; + table_import.tag = WASM_TABLE; + wasmer_table_t *table = NULL; + wasmer_limits_t table_descriptor; + table_descriptor.min = 256; + wasmer_limit_option_t table_max; + table_max.has_some = true; + table_max.some = 256; + table_descriptor.max = table_max; + wasmer_result_t table_result = wasmer_table_new(&table, table_descriptor); + if (table_result != WASMER_OK) + { + print_wasmer_error(); + } + table_import.value.table = table; + + + // Create arbitrary arguments for our program + + + // Read the Wasm file bytes. + FILE *file = fopen("assets/emscripten_hello_world.wasm", "r"); + assert(file); + fseek(file, 0, SEEK_END); + long len = ftell(file); + uint8_t *bytes = malloc(len); + fseek(file, 0, SEEK_SET); + fread(bytes, 1, len, file); + fclose(file); + + wasmer_module_t *module = NULL; + // Compile the WebAssembly module + wasmer_result_t compile_result = wasmer_compile(&module, bytes, len); + printf("Compile result: %d\n", compile_result); + + if (compile_result != WASMER_OK) + { + print_wasmer_error(); + } + + assert(compile_result == WASMER_OK); + + // Set up data for Emscripten + wasmer_emscripten_globals_t *emscripten_globals = wasmer_emscripten_get_emscripten_globals(module); + + if (!emscripten_globals) + { + print_wasmer_error(); + } + assert(emscripten_globals); + + // Create the Emscripten import object + wasmer_import_object_t *import_object = + wasmer_emscripten_generate_import_object(emscripten_globals); + + // Create our imports + wasmer_import_t imports[] = {func_import, global_import, memory_import, table_import}; + int imports_len = sizeof(imports) / sizeof(imports[0]); + // Add our imports to the import object + wasmer_import_object_extend(import_object, imports, imports_len); + + // Instantiatoe the module with our import_object + wasmer_instance_t *instance = NULL; + wasmer_result_t instantiate_result = wasmer_module_import_instantiate(&instance, module, import_object); + printf("Instantiate result: %d\n", instantiate_result); + + if (instantiate_result != WASMER_OK) + { + print_wasmer_error(); + } + assert(instantiate_result == WASMER_OK); + + // Set up emscripten to be called + wasmer_result_t setup_result = wasmer_emscripten_set_up_emscripten(instance, emscripten_globals); + + if (setup_result != WASMER_OK) + { + print_wasmer_error(); + } + + + const char *emscripten_prog_name = "emscripten_test_program"; + const char *emscripten_first_arg = "--help"; + wasmer_byte_array args[] = { + { .bytes = (const uint8_t *) emscripten_prog_name, + .bytes_len = strlen(emscripten_prog_name) }, + { .bytes = (const uint8_t *) emscripten_first_arg, + .bytes_len = strlen(emscripten_first_arg) } + }; + int emscripten_argc = sizeof(args) / sizeof(args[0]); + + wasmer_result_t main_result = wasmer_emscripten_call_main(instance, args, emscripten_argc); + + printf("Main result: %d\n", main_result); + assert(main_result == WASMER_OK); + + + wasmer_import_object_iter_t *func_iter = wasmer_import_object_iterate_functions(import_object); + + puts("Functions in import object:"); + while ( !wasmer_import_object_iter_at_end(func_iter) ) { + wasmer_import_t import; + wasmer_result_t result = wasmer_import_object_iter_next(func_iter, &import); + assert(result == WASMER_OK); + + print_byte_array(&import.module_name); + putchar(' '); + print_byte_array(&import.import_name); + putchar('\n'); + + assert(import.tag == WASM_FUNCTION); + assert(import.value.func); + wasmer_import_object_imports_destroy(&import, 1); + } + wasmer_import_object_iter_destroy(func_iter); + + // Use *_destroy methods to cleanup as specified in the header documentation + wasmer_emscripten_destroy_emscripten_globals(emscripten_globals); + wasmer_import_func_destroy(func); + wasmer_global_destroy(global); + wasmer_memory_destroy(memory); + wasmer_table_destroy(table); + wasmer_instance_destroy(instance); + wasmer_import_object_destroy(import_object); + wasmer_module_destroy(module); + + return 0; +} + diff --git a/lib/runtime-c-api/wasmer.h b/lib/runtime-c-api/wasmer.h index d313f61a9e7..5eacbbd76ca 100644 --- a/lib/runtime-c-api/wasmer.h +++ b/lib/runtime-c-api/wasmer.h @@ -14,6 +14,7 @@ #endif #endif +#define WASMER_EMSCRIPTEN_ENABLED #endif // WASMER_H_MACROS @@ -74,6 +75,15 @@ typedef struct { } wasmer_module_t; +typedef struct { + +} wasmer_instance_t; + +typedef struct { + const uint8_t *bytes; + uint32_t bytes_len; +} wasmer_byte_array; + #if defined(WASMER_EMSCRIPTEN_ENABLED) /** * Type used to construct an import_object_t with Emscripten imports. @@ -94,11 +104,6 @@ typedef struct { } wasmer_export_descriptor_t; -typedef struct { - const uint8_t *bytes; - uint32_t bytes_len; -} wasmer_byte_array; - /** * Opaque pointer to `NamedExportDescriptors`. */ @@ -191,10 +196,6 @@ typedef struct { typedef struct { -} wasmer_instance_t; - -typedef struct { - } wasmer_instance_context_t; typedef struct { @@ -256,6 +257,23 @@ wasmer_result_t wasmer_compile(wasmer_module_t **module, uint8_t *wasm_bytes, uint32_t wasm_bytes_len); +#if defined(WASMER_EMSCRIPTEN_ENABLED) +/** + * Convenience function for setting up arguments and calling the Emscripten + * main function. + * + * WARNING: + * + * Do not call this function on untrusted code when operating without + * additional sandboxing in place. + * Emscripten has access to many host system calls and therefore may do very + * bad things. + */ +wasmer_result_t wasmer_emscripten_call_main(wasmer_instance_t *instance, + const wasmer_byte_array *args, + unsigned int args_len); +#endif + #if defined(WASMER_EMSCRIPTEN_ENABLED) /** * Destroy `wasmer_emscrpten_globals_t` created by @@ -269,6 +287,11 @@ void wasmer_emscripten_destroy_emscripten_globals(wasmer_emscripten_globals_t *g * Create a `wasmer_import_object_t` with Emscripten imports, use * `wasmer_emscripten_get_emscripten_globals` to get a * `wasmer_emscripten_globals_t` from a `wasmer_module_t`. + * + * WARNING: + *1 + * This `import_object_t` contains thin-wrappers around host system calls. + * Do not use this to execute untrusted code without additional sandboxing. */ wasmer_import_object_t *wasmer_emscripten_generate_import_object(wasmer_emscripten_globals_t *globals); #endif @@ -280,6 +303,18 @@ wasmer_import_object_t *wasmer_emscripten_generate_import_object(wasmer_emscript wasmer_emscripten_globals_t *wasmer_emscripten_get_emscripten_globals(const wasmer_module_t *module); #endif +#if defined(WASMER_EMSCRIPTEN_ENABLED) +/** + * Execute global constructors (required if the module is compiled from C++) + * and sets up the internal environment. + * + * This function sets the data pointer in the same way that + * [`wasmer_instance_context_data_set`] does. + */ +wasmer_result_t wasmer_emscripten_set_up_emscripten(wasmer_instance_t *instance, + wasmer_emscripten_globals_t *globals); +#endif + /** * Gets export descriptor kind */ diff --git a/lib/runtime-c-api/wasmer.hh b/lib/runtime-c-api/wasmer.hh index a81b8c77ac5..7a0e6dae2b4 100644 --- a/lib/runtime-c-api/wasmer.hh +++ b/lib/runtime-c-api/wasmer.hh @@ -14,6 +14,7 @@ #endif #endif +#define WASMER_EMSCRIPTEN_ENABLED #endif // WASMER_H_MACROS @@ -61,6 +62,15 @@ struct wasmer_module_t { }; +struct wasmer_instance_t { + +}; + +struct wasmer_byte_array { + const uint8_t *bytes; + uint32_t bytes_len; +}; + #if defined(WASMER_EMSCRIPTEN_ENABLED) /// Type used to construct an import_object_t with Emscripten imports. struct wasmer_emscripten_globals_t { @@ -77,11 +87,6 @@ struct wasmer_export_descriptor_t { }; -struct wasmer_byte_array { - const uint8_t *bytes; - uint32_t bytes_len; -}; - /// Opaque pointer to `NamedExportDescriptors`. struct wasmer_export_descriptors_t { @@ -162,10 +167,6 @@ struct wasmer_import_object_iter_t { }; -struct wasmer_instance_t { - -}; - struct wasmer_instance_context_t { }; @@ -223,6 +224,21 @@ wasmer_result_t wasmer_compile(wasmer_module_t **module, uint8_t *wasm_bytes, uint32_t wasm_bytes_len); +#if defined(WASMER_EMSCRIPTEN_ENABLED) +/// Convenience function for setting up arguments and calling the Emscripten +/// main function. +/// +/// WARNING: +/// +/// Do not call this function on untrusted code when operating without +/// additional sandboxing in place. +/// Emscripten has access to many host system calls and therefore may do very +/// bad things. +wasmer_result_t wasmer_emscripten_call_main(wasmer_instance_t *instance, + const wasmer_byte_array *args, + unsigned int args_len); +#endif + #if defined(WASMER_EMSCRIPTEN_ENABLED) /// Destroy `wasmer_emscrpten_globals_t` created by /// `wasmer_emscripten_get_emscripten_globals`. @@ -233,6 +249,11 @@ void wasmer_emscripten_destroy_emscripten_globals(wasmer_emscripten_globals_t *g /// Create a `wasmer_import_object_t` with Emscripten imports, use /// `wasmer_emscripten_get_emscripten_globals` to get a /// `wasmer_emscripten_globals_t` from a `wasmer_module_t`. +/// +/// WARNING: +///1 +/// This `import_object_t` contains thin-wrappers around host system calls. +/// Do not use this to execute untrusted code without additional sandboxing. wasmer_import_object_t *wasmer_emscripten_generate_import_object(wasmer_emscripten_globals_t *globals); #endif @@ -241,6 +262,16 @@ wasmer_import_object_t *wasmer_emscripten_generate_import_object(wasmer_emscript wasmer_emscripten_globals_t *wasmer_emscripten_get_emscripten_globals(const wasmer_module_t *module); #endif +#if defined(WASMER_EMSCRIPTEN_ENABLED) +/// Execute global constructors (required if the module is compiled from C++) +/// and sets up the internal environment. +/// +/// This function sets the data pointer in the same way that +/// [`wasmer_instance_context_data_set`] does. +wasmer_result_t wasmer_emscripten_set_up_emscripten(wasmer_instance_t *instance, + wasmer_emscripten_globals_t *globals); +#endif + /// Gets export descriptor kind wasmer_import_export_kind wasmer_export_descriptor_kind(wasmer_export_descriptor_t *export_); From a2b47bd5910a3e21eeade70a89b0cbe4bf04359d Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 16 Dec 2019 18:06:37 -0500 Subject: [PATCH 3/5] Update changelog, clean up, fix test --- CHANGELOG.md | 2 + Makefile | 8 ++ lib/runtime-c-api/src/import/emscripten.rs | 11 +- lib/runtime-c-api/src/import/mod.rs | 2 +- lib/runtime-c-api/src/import/wasi.rs | 1 + .../tests/test-emscripten-import-object.c | 101 +----------------- 6 files changed, 23 insertions(+), 102 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aed901cc23d..d219b89917f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## **[Unreleased]** +- [#1062](https://github.com/wasmerio/wasmer/pull/1062) Expose some opt-in Emscripten functions to the C API +- [#1032](https://github.com/wasmerio/wasmer/pull/1032) Change the signature of the Emscripten `abort` function to work with Emscripten 1.38.30 - [#1060](https://github.com/wasmerio/wasmer/pull/1060) Test the capi with all the backends - [#1058](https://github.com/wasmerio/wasmer/pull/1058) Fix minor panic issue when `wasmer::compile_with` called with llvm backend. - [#858](https://github.com/wasmerio/wasmer/pull/858) Minor panic fix when wasmer binary with `loader` option run a module without exported `_start` function. diff --git a/Makefile b/Makefile index d0dc4ec1bb7..40bea0e3a75 100644 --- a/Makefile +++ b/Makefile @@ -114,6 +114,10 @@ capi-llvm: cargo build --manifest-path lib/runtime-c-api/Cargo.toml --release \ --no-default-features --features llvm-backend,wasi +capi-emscripten: + cargo build --manifest-path lib/runtime-c-api/Cargo.toml --release \ + --no-default-features --features singlepass-backend,emscripten + # We use cranelift as the default backend for the capi for now capi: capi-cranelift @@ -129,6 +133,10 @@ test-capi-llvm: capi-llvm cargo test --manifest-path lib/runtime-c-api/Cargo.toml --release \ --no-default-features --features llvm-backend,wasi +test-capi-emscripten: capi-emscripten + cargo test --manifest-path lib/runtime-c-api/Cargo.toml --release \ + --no-default-features --features singlepass-backend,emscripten + test-capi: test-capi-singlepass test-capi-cranelift test-capi-llvm capi-test: test-capi diff --git a/lib/runtime-c-api/src/import/emscripten.rs b/lib/runtime-c-api/src/import/emscripten.rs index 7a9b111ddb0..ca66a630e42 100644 --- a/lib/runtime-c-api/src/import/emscripten.rs +++ b/lib/runtime-c-api/src/import/emscripten.rs @@ -4,7 +4,7 @@ use super::*; use crate::{get_slice_checked, instance::wasmer_instance_t, module::wasmer_module_t}; use std::ptr; -use wasmer_emscripten::EmscriptenGlobals; +use wasmer_emscripten::{EmscriptenData, EmscriptenGlobals}; use wasmer_runtime::{Instance, Module}; /// Type used to construct an import_object_t with Emscripten imports. @@ -55,7 +55,14 @@ pub unsafe extern "C" fn wasmer_emscripten_set_up_emscripten( return wasmer_result_t::WASMER_ERROR; } let instance = &mut *(instance as *mut Instance); - instance.context_mut().data = globals as *mut c_void; + let globals = &*(globals as *mut EmscriptenGlobals); + let em_data = Box::into_raw(Box::new(EmscriptenData::new( + instance, + &globals.data, + Default::default(), + ))) as *mut c_void; + instance.context_mut().data = em_data; + match wasmer_emscripten::set_up_emscripten(instance) { Ok(_) => wasmer_result_t::WASMER_OK, Err(e) => { diff --git a/lib/runtime-c-api/src/import/mod.rs b/lib/runtime-c-api/src/import/mod.rs index 97886211387..1c3c1521c8f 100644 --- a/lib/runtime-c-api/src/import/mod.rs +++ b/lib/runtime-c-api/src/import/mod.rs @@ -8,7 +8,7 @@ use crate::{ value::wasmer_value_tag, wasmer_byte_array, wasmer_result_t, }; -use libc::{c_uchar, c_uint}; +use libc::c_uint; use std::{convert::TryFrom, ffi::c_void, ptr, slice, sync::Arc}; use wasmer_runtime::{Global, Memory, Module, Table}; use wasmer_runtime_core::{ diff --git a/lib/runtime-c-api/src/import/wasi.rs b/lib/runtime-c-api/src/import/wasi.rs index 0ff833a61f5..1dd9d4c15e9 100644 --- a/lib/runtime-c-api/src/import/wasi.rs +++ b/lib/runtime-c-api/src/import/wasi.rs @@ -1,5 +1,6 @@ use super::*; use crate::get_slice_checked; +use libc::c_uchar; use std::{path::PathBuf, ptr, str}; use wasmer_wasi as wasi; diff --git a/lib/runtime-c-api/tests/test-emscripten-import-object.c b/lib/runtime-c-api/tests/test-emscripten-import-object.c index 03b196f59a8..51825daef70 100644 --- a/lib/runtime-c-api/tests/test-emscripten-import-object.c +++ b/lib/runtime-c-api/tests/test-emscripten-import-object.c @@ -35,95 +35,6 @@ void print_byte_array(wasmer_byte_array *arr) { int main() { - // Create a new func to hold the parameter and signature - // of our `host_print` host function - wasmer_value_tag params_sig[] = {WASM_I32, WASM_I32}; - wasmer_value_tag returns_sig[] = {}; - wasmer_import_func_t *func = wasmer_import_func_new((void (*)(void *)) host_print, params_sig, 2, returns_sig, 0); - - // Create module name for our imports - // represented in bytes for UTF-8 compatability - const char *module_name = "env"; - wasmer_byte_array module_name_bytes; - module_name_bytes.bytes = (const uint8_t *) module_name; - module_name_bytes.bytes_len = strlen(module_name); - - // Define a function import - const char *import_name = "host_print"; - wasmer_byte_array import_name_bytes; - import_name_bytes.bytes = (const uint8_t *) import_name; - import_name_bytes.bytes_len = strlen(import_name); - wasmer_import_t func_import; - func_import.module_name = module_name_bytes; - func_import.import_name = import_name_bytes; - func_import.tag = WASM_FUNCTION; - func_import.value.func = func; - - // Define a memory import - const char *import_memory_name = "memory"; - wasmer_byte_array import_memory_name_bytes; - import_memory_name_bytes.bytes = (const uint8_t *) import_memory_name; - import_memory_name_bytes.bytes_len = strlen(import_memory_name); - wasmer_import_t memory_import; - memory_import.module_name = module_name_bytes; - memory_import.import_name = import_memory_name_bytes; - memory_import.tag = WASM_MEMORY; - wasmer_memory_t *memory = NULL; - wasmer_limits_t descriptor; - descriptor.min = 256; - wasmer_limit_option_t max; - max.has_some = true; - max.some = 256; - descriptor.max = max; - wasmer_result_t memory_result = wasmer_memory_new(&memory, descriptor); - if (memory_result != WASMER_OK) - { - print_wasmer_error(); - } - memory_import.value.memory = memory; - - // Define a global import - const char *import_global_name = "__memory_base"; - wasmer_byte_array import_global_name_bytes; - import_global_name_bytes.bytes = (const uint8_t *) import_global_name; - import_global_name_bytes.bytes_len = strlen(import_global_name); - wasmer_import_t global_import; - global_import.module_name = module_name_bytes; - global_import.import_name = import_global_name_bytes; - global_import.tag = WASM_GLOBAL; - wasmer_value_t val; - val.tag = WASM_I32; - val.value.I32 = 1024; - wasmer_global_t *global = wasmer_global_new(val, false); - global_import.value.global = global; - - // Define a table import - const char *import_table_name = "table"; - wasmer_byte_array import_table_name_bytes; - import_table_name_bytes.bytes = (const uint8_t *) import_table_name; - import_table_name_bytes.bytes_len = strlen(import_table_name); - wasmer_import_t table_import; - table_import.module_name = module_name_bytes; - table_import.import_name = import_table_name_bytes; - table_import.tag = WASM_TABLE; - wasmer_table_t *table = NULL; - wasmer_limits_t table_descriptor; - table_descriptor.min = 256; - wasmer_limit_option_t table_max; - table_max.has_some = true; - table_max.some = 256; - table_descriptor.max = table_max; - wasmer_result_t table_result = wasmer_table_new(&table, table_descriptor); - if (table_result != WASMER_OK) - { - print_wasmer_error(); - } - table_import.value.table = table; - - - // Create arbitrary arguments for our program - - // Read the Wasm file bytes. FILE *file = fopen("assets/emscripten_hello_world.wasm", "r"); assert(file); @@ -159,11 +70,6 @@ int main() wasmer_import_object_t *import_object = wasmer_emscripten_generate_import_object(emscripten_globals); - // Create our imports - wasmer_import_t imports[] = {func_import, global_import, memory_import, table_import}; - int imports_len = sizeof(imports) / sizeof(imports[0]); - // Add our imports to the import object - wasmer_import_object_extend(import_object, imports, imports_len); // Instantiatoe the module with our import_object wasmer_instance_t *instance = NULL; @@ -178,11 +84,13 @@ int main() // Set up emscripten to be called wasmer_result_t setup_result = wasmer_emscripten_set_up_emscripten(instance, emscripten_globals); + printf("Set up result: %d\n", setup_result); if (setup_result != WASMER_OK) { print_wasmer_error(); } + assert(setup_result == WASMER_OK); const char *emscripten_prog_name = "emscripten_test_program"; @@ -200,7 +108,6 @@ int main() printf("Main result: %d\n", main_result); assert(main_result == WASMER_OK); - wasmer_import_object_iter_t *func_iter = wasmer_import_object_iterate_functions(import_object); puts("Functions in import object:"); @@ -222,10 +129,6 @@ int main() // Use *_destroy methods to cleanup as specified in the header documentation wasmer_emscripten_destroy_emscripten_globals(emscripten_globals); - wasmer_import_func_destroy(func); - wasmer_global_destroy(global); - wasmer_memory_destroy(memory); - wasmer_table_destroy(table); wasmer_instance_destroy(instance); wasmer_import_object_destroy(import_object); wasmer_module_destroy(module); From cd0b4a3d7f908c964eb836ee18ce3420d7159d71 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 16 Dec 2019 18:07:07 -0500 Subject: [PATCH 4/5] Add capi emscripten test to `test-capi` recipe --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 40bea0e3a75..4b754c724be 100644 --- a/Makefile +++ b/Makefile @@ -137,7 +137,7 @@ test-capi-emscripten: capi-emscripten cargo test --manifest-path lib/runtime-c-api/Cargo.toml --release \ --no-default-features --features singlepass-backend,emscripten -test-capi: test-capi-singlepass test-capi-cranelift test-capi-llvm +test-capi: test-capi-singlepass test-capi-cranelift test-capi-llvm test-capi-emscripten capi-test: test-capi From bb90cffac975c3dced4fd4463d8f54c47cff91d1 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 16 Dec 2019 19:43:57 -0500 Subject: [PATCH 5/5] Address feedback; improve emscripten fn names in C API --- lib/runtime-c-api/src/import/emscripten.rs | 6 +++--- lib/runtime-c-api/tests/test-emscripten-import-object.c | 6 +++--- lib/runtime-c-api/wasmer.h | 8 ++++---- lib/runtime-c-api/wasmer.hh | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/runtime-c-api/src/import/emscripten.rs b/lib/runtime-c-api/src/import/emscripten.rs index ca66a630e42..83777a762a5 100644 --- a/lib/runtime-c-api/src/import/emscripten.rs +++ b/lib/runtime-c-api/src/import/emscripten.rs @@ -13,7 +13,7 @@ pub struct wasmer_emscripten_globals_t; /// Create a `wasmer_emscripten_globals_t` from a Wasm module. #[no_mangle] -pub unsafe extern "C" fn wasmer_emscripten_get_emscripten_globals( +pub unsafe extern "C" fn wasmer_emscripten_get_globals( module: *const wasmer_module_t, ) -> *mut wasmer_emscripten_globals_t { if module.is_null() { @@ -32,7 +32,7 @@ pub unsafe extern "C" fn wasmer_emscripten_get_emscripten_globals( /// Destroy `wasmer_emscrpten_globals_t` created by /// `wasmer_emscripten_get_emscripten_globals`. #[no_mangle] -pub unsafe extern "C" fn wasmer_emscripten_destroy_emscripten_globals( +pub unsafe extern "C" fn wasmer_emscripten_destroy_globals( globals: *mut wasmer_emscripten_globals_t, ) { if globals.is_null() { @@ -47,7 +47,7 @@ pub unsafe extern "C" fn wasmer_emscripten_destroy_emscripten_globals( /// This function sets the data pointer in the same way that /// [`wasmer_instance_context_data_set`] does. #[no_mangle] -pub unsafe extern "C" fn wasmer_emscripten_set_up_emscripten( +pub unsafe extern "C" fn wasmer_emscripten_set_up( instance: *mut wasmer_instance_t, globals: *mut wasmer_emscripten_globals_t, ) -> wasmer_result_t { diff --git a/lib/runtime-c-api/tests/test-emscripten-import-object.c b/lib/runtime-c-api/tests/test-emscripten-import-object.c index 51825daef70..bbc581db3e5 100644 --- a/lib/runtime-c-api/tests/test-emscripten-import-object.c +++ b/lib/runtime-c-api/tests/test-emscripten-import-object.c @@ -58,7 +58,7 @@ int main() assert(compile_result == WASMER_OK); // Set up data for Emscripten - wasmer_emscripten_globals_t *emscripten_globals = wasmer_emscripten_get_emscripten_globals(module); + wasmer_emscripten_globals_t *emscripten_globals = wasmer_emscripten_get_globals(module); if (!emscripten_globals) { @@ -83,7 +83,7 @@ int main() assert(instantiate_result == WASMER_OK); // Set up emscripten to be called - wasmer_result_t setup_result = wasmer_emscripten_set_up_emscripten(instance, emscripten_globals); + wasmer_result_t setup_result = wasmer_emscripten_set_up(instance, emscripten_globals); printf("Set up result: %d\n", setup_result); if (setup_result != WASMER_OK) @@ -128,7 +128,7 @@ int main() wasmer_import_object_iter_destroy(func_iter); // Use *_destroy methods to cleanup as specified in the header documentation - wasmer_emscripten_destroy_emscripten_globals(emscripten_globals); + wasmer_emscripten_destroy_globals(emscripten_globals); wasmer_instance_destroy(instance); wasmer_import_object_destroy(import_object); wasmer_module_destroy(module); diff --git a/lib/runtime-c-api/wasmer.h b/lib/runtime-c-api/wasmer.h index 5eacbbd76ca..77c888abe6e 100644 --- a/lib/runtime-c-api/wasmer.h +++ b/lib/runtime-c-api/wasmer.h @@ -279,7 +279,7 @@ wasmer_result_t wasmer_emscripten_call_main(wasmer_instance_t *instance, * Destroy `wasmer_emscrpten_globals_t` created by * `wasmer_emscripten_get_emscripten_globals`. */ -void wasmer_emscripten_destroy_emscripten_globals(wasmer_emscripten_globals_t *globals); +void wasmer_emscripten_destroy_globals(wasmer_emscripten_globals_t *globals); #endif #if defined(WASMER_EMSCRIPTEN_ENABLED) @@ -300,7 +300,7 @@ wasmer_import_object_t *wasmer_emscripten_generate_import_object(wasmer_emscript /** * Create a `wasmer_emscripten_globals_t` from a Wasm module. */ -wasmer_emscripten_globals_t *wasmer_emscripten_get_emscripten_globals(const wasmer_module_t *module); +wasmer_emscripten_globals_t *wasmer_emscripten_get_globals(const wasmer_module_t *module); #endif #if defined(WASMER_EMSCRIPTEN_ENABLED) @@ -311,8 +311,8 @@ wasmer_emscripten_globals_t *wasmer_emscripten_get_emscripten_globals(const wasm * This function sets the data pointer in the same way that * [`wasmer_instance_context_data_set`] does. */ -wasmer_result_t wasmer_emscripten_set_up_emscripten(wasmer_instance_t *instance, - wasmer_emscripten_globals_t *globals); +wasmer_result_t wasmer_emscripten_set_up(wasmer_instance_t *instance, + wasmer_emscripten_globals_t *globals); #endif /** diff --git a/lib/runtime-c-api/wasmer.hh b/lib/runtime-c-api/wasmer.hh index 7a0e6dae2b4..04c2168afac 100644 --- a/lib/runtime-c-api/wasmer.hh +++ b/lib/runtime-c-api/wasmer.hh @@ -242,7 +242,7 @@ wasmer_result_t wasmer_emscripten_call_main(wasmer_instance_t *instance, #if defined(WASMER_EMSCRIPTEN_ENABLED) /// Destroy `wasmer_emscrpten_globals_t` created by /// `wasmer_emscripten_get_emscripten_globals`. -void wasmer_emscripten_destroy_emscripten_globals(wasmer_emscripten_globals_t *globals); +void wasmer_emscripten_destroy_globals(wasmer_emscripten_globals_t *globals); #endif #if defined(WASMER_EMSCRIPTEN_ENABLED) @@ -259,7 +259,7 @@ wasmer_import_object_t *wasmer_emscripten_generate_import_object(wasmer_emscript #if defined(WASMER_EMSCRIPTEN_ENABLED) /// Create a `wasmer_emscripten_globals_t` from a Wasm module. -wasmer_emscripten_globals_t *wasmer_emscripten_get_emscripten_globals(const wasmer_module_t *module); +wasmer_emscripten_globals_t *wasmer_emscripten_get_globals(const wasmer_module_t *module); #endif #if defined(WASMER_EMSCRIPTEN_ENABLED) @@ -268,8 +268,8 @@ wasmer_emscripten_globals_t *wasmer_emscripten_get_emscripten_globals(const wasm /// /// This function sets the data pointer in the same way that /// [`wasmer_instance_context_data_set`] does. -wasmer_result_t wasmer_emscripten_set_up_emscripten(wasmer_instance_t *instance, - wasmer_emscripten_globals_t *globals); +wasmer_result_t wasmer_emscripten_set_up(wasmer_instance_t *instance, + wasmer_emscripten_globals_t *globals); #endif /// Gets export descriptor kind