diff --git a/Cargo.lock b/Cargo.lock index 6d301f57f..28e309176 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -81,7 +81,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "block-buffer" -version = "0.7.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "block-padding 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -113,6 +113,21 @@ name = "cast" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cbindgen" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.58 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cc" version = "1.0.28" @@ -708,6 +723,14 @@ dependencies = [ "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "remove_dir_all" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rustc-demangle" version = "0.1.13" @@ -748,6 +771,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "serde" version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.58 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "serde-bench" @@ -791,7 +817,7 @@ name = "sha2" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "block-buffer 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -876,6 +902,19 @@ dependencies = [ "serde_json 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tempfile" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "termcolor" version = "1.0.4" @@ -920,6 +959,14 @@ dependencies = [ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "toml" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "typenum" version = "1.10.0" @@ -1053,6 +1100,16 @@ dependencies = [ "wasmer-runtime-core 0.1.2", ] +[[package]] +name = "wasmer-runtime-c-api" +version = "0.1.4" +dependencies = [ + "cbindgen 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime 0.1.4", + "wasmer-runtime-core 0.1.2", +] + [[package]] name = "wasmer-runtime-core" version = "0.1.2" @@ -1172,11 +1229,12 @@ dependencies = [ "checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" "checksum bindgen 0.46.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f7f7f0701772b17de73e4f5cbcb1dd6926f4706cba4c1ab62c5367f8bdc94e1" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" -"checksum block-buffer 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "509de513cca6d92b6aacf9c61acfe7eaa160837323a81068d690cc1f8e5740da" +"checksum block-buffer 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49665c62e0e700857531fa5d3763e91b539ff1abeebd56808d378b495870d60d" "checksum block-padding 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d75255892aeb580d3c566f213a2b6fdc1c66667839f45719ee1d30ebf2aea591" "checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" "checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427" +"checksum cbindgen 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "32e01024aaf5390d6a8145047371a4f5b0063a14c1e411bc731353bd2278ca44" "checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749" "checksum cexpr 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "644d693ecfa91955ed32dcc7eda4914e1be97a641fb6f0645a37348e20b230da" "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" @@ -1248,6 +1306,7 @@ dependencies = [ "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f" "checksum regex-syntax 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8c2f35eedad5295fdf00a63d7d4b238135723f92b434ec06774dad15c7ab0861" +"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" "checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" @@ -1269,11 +1328,13 @@ dependencies = [ "checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9" "checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" "checksum target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4af5e2227f0b887d591d3724b796a96eff04226104d872f5b3883fcd427d64b9" +"checksum tempfile 3.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "37daa55a7240c4931c84559f03b3cad7d19535840d1c4a0cc4e9b2fb0dcf70ff" "checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" +"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" "checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1" diff --git a/Cargo.toml b/Cargo.toml index 9d96e28cd..67fbda1b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ wasmer-runtime-core = { path = "lib/runtime-core" } wasmer-emscripten = { path = "lib/emscripten" } [workspace] -members = ["lib/clif-backend", "lib/runtime", "lib/runtime-core", "lib/emscripten", "lib/spectests", "lib/win-exception-handler"] +members = ["lib/clif-backend", "lib/runtime", "lib/runtime-core", "lib/emscripten", "lib/spectests", "lib/win-exception-handler", "lib/runtime-c-api"] [build-dependencies] wabt = "0.7.2" diff --git a/Makefile b/Makefile index c176dc7d1..2506f196c 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,9 @@ spectests: emtests: WASM_EMSCRIPTEN_GENERATE_EMTESTS=1 cargo build -p wasmer-emscripten +capi: + WASM_EMSCRIPTEN_GENERATE_C_API_HEADERS=1 cargo build --manifest-path lib/runtime-c-api/Cargo.toml --features generate-c-api-headers + # clean: # rm -rf artifacts @@ -34,9 +37,10 @@ precommit: lint test test: # We use one thread so the emscripten stdouts doesn't collide - cargo test --all -- --test-threads=1 $(runargs) + cargo test --all --exclude wasmer-runtime-c-api -- --test-threads=1 $(runargs) # cargo test --all --exclude wasmer-emscripten -- --test-threads=1 $(runargs) - # cargo test -p wasmer-spectests -- --test-threads=1 $(runargs) + cargo build -p wasmer-runtime-c-api + cargo test -p wasmer-runtime-c-api -- --nocapture release: # If you are in OS-X, you will need mingw-w64 for cross compiling to windows diff --git a/lib/runtime-c-api/Cargo.toml b/lib/runtime-c-api/Cargo.toml new file mode 100644 index 000000000..692c03cd7 --- /dev/null +++ b/lib/runtime-c-api/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "wasmer-runtime-c-api" +version = "0.1.4" +description = "Wasmer c-api library" +license = "MIT" +authors = ["The Wasmer Engineering Team "] +repository = "https://github.com/wasmerio/wasmer" +edition = "2018" +readme = "README.md" + +[dependencies] +wasmer-runtime = { path = "../runtime", version = "0.1.2" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.1.2" } +libc = "0.2" + +[lib] +crate-type = ["cdylib"] + +[build-dependencies] +cbindgen = { version = "0.7.1", optional = true } + +[features] +generate-c-api-headers = ["cbindgen"] + + diff --git a/lib/runtime-c-api/README.md b/lib/runtime-c-api/README.md new file mode 100644 index 000000000..02ba9a705 --- /dev/null +++ b/lib/runtime-c-api/README.md @@ -0,0 +1,10 @@ +# Wasmer Runtime C API + +## Generating header files +Run `make capi` from wasmer project root directory + +## Running tests +The tests can be run via `cargo test`, E.g. `cargo test -p wasmer-runtime-c-api -- --nocapture` + +*Running manually* +`cmake . && make && make test` from the `lib/runtime-c-api/tests` directory diff --git a/lib/runtime-c-api/build.rs b/lib/runtime-c-api/build.rs new file mode 100644 index 000000000..f78c5cf69 --- /dev/null +++ b/lib/runtime-c-api/build.rs @@ -0,0 +1,39 @@ +#[cfg(feature = "generate-c-api-headers")] +extern crate cbindgen; + +use std::env; + +static CAPI_ENV_VAR: &str = "WASM_EMSCRIPTEN_GENERATE_C_API_HEADERS"; + +fn main() { + if env::var(CAPI_ENV_VAR).unwrap_or("0".to_string()) == "1" { + build(); + } +} + +#[cfg(feature = "generate-c-api-headers")] +fn build() { + let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + + use cbindgen::Language; + cbindgen::Builder::new() + .with_crate(crate_dir.clone()) + .with_language(Language::C) + .with_include_guard("WASMER_H") + .generate() + .expect("Unable to generate C bindings") + .write_to_file("wasmer.h"); + + cbindgen::Builder::new() + .with_crate(crate_dir) + .with_language(Language::Cxx) + .with_include_guard("WASMER_H") + .generate() + .expect("Unable to generate C++ bindings") + .write_to_file("wasmer.hh"); +} + +#[cfg(not(feature = "generate-c-api-headers"))] +fn build() { + panic!("environment var set to generate wasmer c API headers but generate-c-api-headers feature not enabled") +} diff --git a/lib/runtime-c-api/src/lib.rs b/lib/runtime-c-api/src/lib.rs new file mode 100644 index 000000000..5b1dd1955 --- /dev/null +++ b/lib/runtime-c-api/src/lib.rs @@ -0,0 +1,1105 @@ +extern crate wasmer_runtime; +extern crate wasmer_runtime_core; + +use libc::{c_char, c_int, int32_t, int64_t, uint32_t, uint8_t}; +use std::cell::RefCell; +use std::collections::HashMap; +use std::error::Error; +use std::ffi::CStr; +use std::fmt; +use std::slice; +use std::sync::Arc; +use std::{ffi::c_void, ptr}; +use wasmer_runtime::{Ctx, Global, ImportObject, Instance, Memory, Module, Table, Value}; +use wasmer_runtime_core::export::{Context, Export, FuncPointer}; +use wasmer_runtime_core::import::Namespace; +use wasmer_runtime_core::types::{ElementType, FuncSig, MemoryDescriptor, TableDescriptor, Type}; +use wasmer_runtime_core::units::{Bytes, Pages}; + +#[allow(non_camel_case_types)] +pub struct wasmer_import_object_t(); + +#[allow(non_camel_case_types)] +pub struct wasmer_module_t(); + +#[allow(non_camel_case_types)] +pub struct wasmer_instance_t(); + +#[allow(non_camel_case_types)] +pub struct wasmer_instance_context_t(); + +#[allow(non_camel_case_types)] +#[no_mangle] +#[repr(C)] +pub enum wasmer_result_t { + WASMER_OK = 1, + WASMER_ERROR = 2, +} + +#[repr(u32)] +#[derive(Clone)] +pub enum wasmer_value_tag { + WASM_I32, + WASM_I64, + WASM_F32, + WASM_F64, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union wasmer_value { + I32: int32_t, + I64: int64_t, + F32: f32, + F64: f64, +} + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_value_t { + tag: wasmer_value_tag, + value: wasmer_value, +} + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_global_descriptor_t { + mutable: bool, + kind: wasmer_value_tag, +} + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_memory_t(); + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_table_t(); + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_func_t(); + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_global_t(); + +#[repr(C)] +pub struct wasmer_limits_t { + pub min: uint32_t, + pub max: wasmer_limit_option_t, +} + +#[repr(C)] +pub struct wasmer_limit_option_t { + pub has_some: bool, + pub some: uint32_t, +} + +#[repr(C)] +pub struct wasmer_func_signature { + pub params: *const wasmer_value_tag, + pub params_len: c_int, + pub returns: *const wasmer_value_tag, + pub returns_len: c_int, +} + +#[repr(C)] +pub struct wasmer_import_t { + module_name: wasmer_byte_array, + import_name: wasmer_byte_array, + tag: wasmer_import_export_kind, + value: wasmer_import_export_value, +} + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_export_t; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_exports_t; + +#[repr(u32)] +#[derive(Clone)] +pub enum wasmer_import_export_kind { + WASM_FUNCTION, + WASM_GLOBAL, + WASM_MEMORY, + WASM_TABLE, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union wasmer_import_export_value { + func: *const wasmer_func_t, + table: *const wasmer_table_t, + memory: *const wasmer_memory_t, + global: *const wasmer_global_t, +} + +#[repr(C)] +pub struct wasmer_byte_array { + bytes: *const uint8_t, + bytes_len: uint32_t, +} + +/// Returns true for valid wasm bytes and false for invalid bytes +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_validate( + wasm_bytes: *mut uint8_t, + wasm_bytes_len: uint32_t, +) -> bool { + if wasm_bytes.is_null() { + return false; + } + let bytes: &[u8] = + unsafe { ::std::slice::from_raw_parts_mut(wasm_bytes, wasm_bytes_len as usize) }; + wasmer_runtime_core::validate(bytes) +} + +/// Creates a new Memory for the given descriptor and initializes the given +/// pointer to pointer to a pointer to the new memory. +/// +/// The caller owns the object and should call `wasmer_memory_destroy` to free it. +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[no_mangle] +pub unsafe extern "C" fn wasmer_memory_new( + mut memory: *mut *mut wasmer_memory_t, + limits: wasmer_limits_t, +) -> wasmer_result_t { + let max = if limits.max.has_some { + Some(Pages(limits.max.some)) + } else { + None + }; + let desc = MemoryDescriptor { + minimum: Pages(limits.min), + maximum: max, + shared: false, + }; + let result = Memory::new(desc); + let new_memory = match result { + Ok(memory) => memory, + Err(error) => { + update_last_error(error); + return wasmer_result_t::WASMER_ERROR; + } + }; + unsafe { *memory = Box::into_raw(Box::new(new_memory)) as *mut wasmer_memory_t }; + wasmer_result_t::WASMER_OK +} + +/// Grows a Memory by the given number of pages. +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_memory_grow( + memory: *mut wasmer_memory_t, + delta: uint32_t, +) -> wasmer_result_t { + let memory = unsafe { &*(memory as *mut Memory) }; + let maybe_delta = memory.grow(Pages(delta)); + if let Some(_delta) = maybe_delta { + wasmer_result_t::WASMER_OK + } else { + update_last_error(CApiError { + msg: "unable to grow memory".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } +} + +/// Returns the current length in pages of the given memory +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_memory_length(memory: *mut wasmer_memory_t) -> uint32_t { + let memory = unsafe { &*(memory as *mut Memory) }; + let Pages(len) = memory.size(); + len +} + +/// Creates a new Table for the given descriptor and initializes the given +/// pointer to pointer to a pointer to the new Table. +/// +/// The caller owns the object and should call `wasmer_table_destroy` to free it. +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[no_mangle] +pub unsafe extern "C" fn wasmer_table_new( + mut table: *mut *mut wasmer_table_t, + limits: wasmer_limits_t, +) -> wasmer_result_t { + let max = if limits.max.has_some { + Some(limits.max.some) + } else { + None + }; + let desc = TableDescriptor { + element: ElementType::Anyfunc, + minimum: limits.min, + maximum: max, + }; + let result = Table::new(desc); + let new_table = match result { + Ok(table) => table, + Err(error) => { + update_last_error(error); + return wasmer_result_t::WASMER_ERROR; + } + }; + unsafe { *table = Box::into_raw(Box::new(new_table)) as *mut wasmer_table_t }; + wasmer_result_t::WASMER_OK +} + +/// Grows a Table by the given number of elements. +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_table_grow( + table: *mut wasmer_table_t, + delta: uint32_t, +) -> wasmer_result_t { + let table = unsafe { &*(table as *mut Table) }; + let maybe_delta = table.grow(delta); + if let Some(_delta) = maybe_delta { + wasmer_result_t::WASMER_OK + } else { + update_last_error(CApiError { + msg: "unable to grow table".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } +} + +/// Returns the current length of the given Table +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_table_length(table: *mut wasmer_table_t) -> uint32_t { + let table = unsafe { &*(table as *mut Table) }; + let len = table.size(); + len +} + +/// Frees memory for the given Table +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_table_destroy(table: *mut wasmer_table_t) { + if !table.is_null() { + drop(unsafe { Box::from_raw(table as *mut Table) }); + } +} + +/// Creates a new Global and returns a pointer to it. +/// The caller owns the object and should call `wasmer_global_destroy` to free it. +#[no_mangle] +pub unsafe extern "C" fn wasmer_global_new( + value: wasmer_value_t, + mutable: bool, +) -> *mut wasmer_global_t { + let global = if mutable { + Global::new_mutable(value.into()) + } else { + Global::new(value.into()) + }; + unsafe { Box::into_raw(Box::new(global)) as *mut wasmer_global_t } +} + +/// Gets the value stored by the given Global +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_global_get(global: *mut wasmer_global_t) -> wasmer_value_t { + let global = unsafe { &*(global as *mut Global) }; + let value: wasmer_value_t = global.get().into(); + value +} + +/// Sets the value stored by the given Global +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_global_set(global: *mut wasmer_global_t, value: wasmer_value_t) { + let global = unsafe { &*(global as *mut Global) }; + global.set(value.into()); +} + +/// Returns a descriptor (type, mutability) of the given Global +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_global_get_descriptor( + global: *mut wasmer_global_t, +) -> wasmer_global_descriptor_t { + let global = unsafe { &*(global as *mut Global) }; + let descriptor = global.descriptor(); + let desc = wasmer_global_descriptor_t { + mutable: descriptor.mutable, + kind: descriptor.ty.into(), + }; + desc +} + +/// Frees memory for the given Global +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_global_destroy(global: *mut wasmer_global_t) { + if !global.is_null() { + drop(unsafe { Box::from_raw(global as *mut Global) }); + } +} + +/// Frees memory for the given Memory +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_memory_destroy(memory: *mut wasmer_memory_t) { + if !memory.is_null() { + drop(unsafe { Box::from_raw(memory as *mut Memory) }); + } +} + +/// Creates a new Module from the given wasm bytes. +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_compile( + mut module: *mut *mut wasmer_module_t, + wasm_bytes: *mut uint8_t, + wasm_bytes_len: uint32_t, +) -> wasmer_result_t { + let bytes: &[u8] = + unsafe { ::std::slice::from_raw_parts_mut(wasm_bytes, wasm_bytes_len as usize) }; + let result = wasmer_runtime::compile(bytes); + let new_module = match result { + Ok(instance) => instance, + Err(error) => { + update_last_error(error); + return wasmer_result_t::WASMER_ERROR; + } + }; + unsafe { *module = Box::into_raw(Box::new(new_module)) as *mut wasmer_module_t }; + wasmer_result_t::WASMER_OK +} + +/// Frees memory for the given Module +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_module_destroy(module: *mut wasmer_module_t) { + if !module.is_null() { + drop(unsafe { Box::from_raw(module as *mut Module) }); + } +} + +/// Creates a new Instance from the given wasm bytes and imports. +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_instantiate( + mut instance: *mut *mut wasmer_instance_t, + wasm_bytes: *mut uint8_t, + wasm_bytes_len: uint32_t, + imports: *mut wasmer_import_t, + imports_len: c_int, +) -> wasmer_result_t { + if wasm_bytes.is_null() { + update_last_error(CApiError { + msg: "wasm bytes ptr is null".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + } + let imports: &[wasmer_import_t] = slice::from_raw_parts(imports, imports_len as usize); + let mut import_object = ImportObject::new(); + let mut namespaces = HashMap::new(); + for import in imports { + let module_name = slice::from_raw_parts( + import.module_name.bytes, + import.module_name.bytes_len as usize, + ); + let module_name = if let Ok(s) = std::str::from_utf8(module_name) { + s + } else { + update_last_error(CApiError { + msg: "error converting module name to string".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + }; + let import_name = slice::from_raw_parts( + import.import_name.bytes, + import.import_name.bytes_len as usize, + ); + let import_name = if let Ok(s) = std::str::from_utf8(import_name) { + s + } else { + update_last_error(CApiError { + msg: "error converting import_name to string".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + }; + + let namespace = namespaces + .entry(module_name) + .or_insert_with(|| Namespace::new()); + + let export = match import.tag { + wasmer_import_export_kind::WASM_MEMORY => import.value.memory as *mut Export, + wasmer_import_export_kind::WASM_FUNCTION => import.value.func as *mut Export, + wasmer_import_export_kind::WASM_GLOBAL => import.value.global as *mut Export, + wasmer_import_export_kind::WASM_TABLE => import.value.table as *mut Export, + }; + namespace.insert(import_name, unsafe { (&*export).clone() }); + } + for (module_name, namespace) in namespaces.into_iter() { + import_object.register(module_name, namespace); + } + + let bytes: &[u8] = + unsafe { ::std::slice::from_raw_parts_mut(wasm_bytes, wasm_bytes_len as usize) }; + let result = wasmer_runtime::instantiate(bytes, &import_object); + let new_instance = match result { + Ok(instance) => instance, + Err(error) => { + // TODO the trait bound `wasmer_runtime::error::Error: std::error::Error` is not satisfied + //update_last_error(error); + update_last_error(CApiError { + msg: "error instantiating".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + } + }; + unsafe { *instance = Box::into_raw(Box::new(new_instance)) as *mut wasmer_instance_t }; + wasmer_result_t::WASMER_OK +} + +/// Calls an instances exported function by `name` with the provided parameters. +/// Results are set using the provided `results` pointer. +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_instance_call( + instance: *mut wasmer_instance_t, + name: *const c_char, + params: *const wasmer_value_t, + params_len: c_int, + results: *mut wasmer_value_t, + results_len: c_int, +) -> wasmer_result_t { + if instance.is_null() { + update_last_error(CApiError { + msg: "instance ptr is null".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + } + if name.is_null() { + update_last_error(CApiError { + msg: "name ptr is null".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + } + if params.is_null() { + update_last_error(CApiError { + msg: "params ptr is null".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + } + + let params: &[wasmer_value_t] = slice::from_raw_parts(params, params_len as usize); + let params: Vec = params.iter().cloned().map(|x| x.into()).collect(); + + let func_name_c = unsafe { CStr::from_ptr(name) }; + let func_name_r = func_name_c.to_str().unwrap(); + + let results: &mut [wasmer_value_t] = slice::from_raw_parts_mut(results, results_len as usize); + let result = (&*(instance as *mut Instance)).call(func_name_r, ¶ms[..]); + + match result { + Ok(results_vec) => { + if results_vec.len() > 0 { + let ret = match results_vec[0] { + Value::I32(x) => wasmer_value_t { + tag: wasmer_value_tag::WASM_I32, + value: wasmer_value { I32: x }, + }, + Value::I64(x) => wasmer_value_t { + tag: wasmer_value_tag::WASM_I64, + value: wasmer_value { I64: x }, + }, + Value::F32(x) => wasmer_value_t { + tag: wasmer_value_tag::WASM_F32, + value: wasmer_value { F32: x }, + }, + Value::F64(x) => wasmer_value_t { + tag: wasmer_value_tag::WASM_F64, + value: wasmer_value { F64: x }, + }, + }; + results[0] = ret; + } + wasmer_result_t::WASMER_OK + } + Err(err) => { + update_last_error(err); + wasmer_result_t::WASMER_ERROR + } + } +} + +/// Gets Exports for the given instance +/// +/// The caller owns the object and should call `wasmer_exports_destroy` to free it. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_instance_exports( + instance: *mut wasmer_instance_t, + exports: *mut *mut wasmer_exports_t, +) { + let mut instance = unsafe { &mut *(instance as *mut Instance) }; + let named_exports: Box = + Box::new(NamedExports(instance.exports().map(|e| e.into()).collect())); + unsafe { *exports = Box::into_raw(named_exports) as *mut wasmer_exports_t }; +} + +pub struct NamedExports(Vec); + +/// Frees the memory for the given exports +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_exports_destroy(exports: *mut wasmer_exports_t) { + if !exports.is_null() { + drop(unsafe { Box::from_raw(exports as *mut NamedExports) }); + } +} + +/// Gets the length of the exports +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_exports_len(exports: *mut wasmer_exports_t) -> c_int { + if exports.is_null() { + return 0; + } + (*(exports as *mut NamedExports)).0.len() as c_int +} + +/// Gets wasmer_export by index +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_exports_get( + exports: *mut wasmer_exports_t, + idx: c_int, +) -> *mut wasmer_export_t { + if exports.is_null() { + return ptr::null_mut(); + } + let mut named_exports = unsafe { &mut *(exports as *mut NamedExports) }; + let ptr = &mut (*named_exports).0[idx as usize] as *mut NamedExport as *mut wasmer_export_t; + ptr +} + +/// Gets wasmer_export kind +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_export_kind( + export: *mut wasmer_export_t, +) -> wasmer_import_export_kind { + let named_export = &*(export as *mut NamedExport); + match named_export.export { + Export::Table(_) => wasmer_import_export_kind::WASM_TABLE, + Export::Function { .. } => wasmer_import_export_kind::WASM_FUNCTION, + Export::Global(_) => wasmer_import_export_kind::WASM_GLOBAL, + Export::Memory(_) => wasmer_import_export_kind::WASM_MEMORY, + } +} + +/// Creates new func +/// +/// The caller owns the object and should call `wasmer_func_destroy` to free it. +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_func_new( + func: extern "C" fn(data: *mut c_void), + params: *const wasmer_value_tag, + params_len: c_int, + returns: *const wasmer_value_tag, + returns_len: c_int, +) -> *const wasmer_func_t { + let params: &[wasmer_value_tag] = slice::from_raw_parts(params, params_len as usize); + let params: Vec = params.iter().cloned().map(|x| x.into()).collect(); + let returns: &[wasmer_value_tag] = slice::from_raw_parts(returns, returns_len as usize); + let returns: Vec = returns.iter().cloned().map(|x| x.into()).collect(); + + let export = Box::new(Export::Function { + func: unsafe { FuncPointer::new(func as _) }, + ctx: Context::Internal, + signature: Arc::new(FuncSig::new(params, returns)), + }); + Box::into_raw(export) as *mut wasmer_func_t +} + +/// Sets the result parameter to the arity of the params of the wasmer_func_t +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_func_params_arity( + func: *mut wasmer_func_t, + result: *mut uint32_t, +) -> wasmer_result_t { + let mut export = unsafe { &mut *(func as *mut Export) }; + let result = if let Export::Function { ref signature, .. } = *export { + unsafe { *result = signature.params().len() as uint32_t }; + wasmer_result_t::WASMER_OK + } else { + update_last_error(CApiError { + msg: "func ptr error in wasmer_func_params_arity".to_string(), + }); + wasmer_result_t::WASMER_ERROR + }; + result +} + +/// Sets the params buffer to the parameter types of the given wasmer_func_t +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_func_params( + func: *mut wasmer_func_t, + params: *mut wasmer_value_tag, + params_len: c_int, +) -> wasmer_result_t { + let mut export = unsafe { &mut *(func as *mut Export) }; + let result = if let Export::Function { ref signature, .. } = *export { + let params: &mut [wasmer_value_tag] = + slice::from_raw_parts_mut(params, params_len as usize); + for (i, item) in signature.params().iter().enumerate() { + params[i] = item.into(); + } + wasmer_result_t::WASMER_OK + } else { + update_last_error(CApiError { + msg: "func ptr error in wasmer_func_params".to_string(), + }); + wasmer_result_t::WASMER_ERROR + }; + result +} + +/// Sets the returns buffer to the parameter types of the given wasmer_func_t +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_func_returns( + func: *mut wasmer_func_t, + returns: *mut wasmer_value_tag, + returns_len: c_int, +) -> wasmer_result_t { + let mut export = unsafe { &mut *(func as *mut Export) }; + let result = if let Export::Function { ref signature, .. } = *export { + let returns: &mut [wasmer_value_tag] = + slice::from_raw_parts_mut(returns, returns_len as usize); + for (i, item) in signature.returns().iter().enumerate() { + returns[i] = item.into(); + } + wasmer_result_t::WASMER_OK + } else { + update_last_error(CApiError { + msg: "func ptr error in wasmer_func_returns".to_string(), + }); + wasmer_result_t::WASMER_ERROR + }; + result +} + +/// Sets the result parameter to the arity of the returns of the wasmer_func_t +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_func_returns_arity( + func: *mut wasmer_func_t, + result: *mut uint32_t, +) -> wasmer_result_t { + let mut export = unsafe { &*(func as *mut Export) }; + let result = if let Export::Function { ref signature, .. } = *export { + unsafe { *result = signature.returns().len() as uint32_t }; + wasmer_result_t::WASMER_OK + } else { + update_last_error(CApiError { + msg: "func ptr error in wasmer_func_results_arity".to_string(), + }); + wasmer_result_t::WASMER_ERROR + }; + result +} + +/// Frees memory for the given Func +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_func_destroy(func: *mut wasmer_func_t) { + if !func.is_null() { + drop(unsafe { Box::from_raw(func as *mut Export) }); + } +} + +/// Gets func from wasm_export +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_export_to_func( + export: *mut wasmer_export_t, +) -> *const wasmer_func_t { + let named_export = &*(export as *mut NamedExport); + &named_export.export as *const Export as *const wasmer_func_t +} + +/// Gets name from wasmer_export +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_export_name(export: *mut wasmer_export_t) -> wasmer_byte_array { + let named_export = &*(export as *mut NamedExport); + wasmer_byte_array { + bytes: named_export.name.as_ptr(), + bytes_len: named_export.name.len() as u32, + } +} + +/// Calls a `func` with the provided parameters. +/// Results are set using the provided `results` pointer. +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_func_call( + func: *mut wasmer_func_t, + params: *const wasmer_value_t, + params_len: c_int, + results: *mut wasmer_value_t, + results_len: c_int, +) -> wasmer_result_t { + if func.is_null() { + update_last_error(CApiError { + msg: "func ptr is null".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + } + if params.is_null() { + update_last_error(CApiError { + msg: "params ptr is null".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + } + + let params: &[wasmer_value_t] = slice::from_raw_parts(params, params_len as usize); + let params: Vec = params.iter().cloned().map(|x| x.into()).collect(); + + let export_func = unsafe { &*(func as *mut Export) }; + + let results: &mut [wasmer_value_t] = slice::from_raw_parts_mut(results, results_len as usize); + // TODO implement func.call + update_last_error(CApiError { + msg: "wasmer_func_call not yet implemented".to_string(), + }); + wasmer_result_t::WASMER_ERROR + // let result = instance.call(func_name_r, ¶ms[..]); + // Box::into_raw(export_func); + // match result { + // Ok(results_vec) => { + // if results_vec.len() > 0 { + // let ret = match results_vec[0] { + // Value::I32(x) => wasmer_value_t { + // tag: wasmer_value_tag::WASM_I32, + // value: wasmer_value { I32: x }, + // }, + // Value::I64(x) => wasmer_value_t { + // tag: wasmer_value_tag::WASM_I64, + // value: wasmer_value { I64: x }, + // }, + // Value::F32(x) => wasmer_value_t { + // tag: wasmer_value_tag::WASM_F32, + // value: wasmer_value { F32: x }, + // }, + // Value::F64(x) => wasmer_value_t { + // tag: wasmer_value_tag::WASM_F64, + // value: wasmer_value { F64: x }, + // }, + // }; + // results[0] = ret; + // } + // wasmer_result_t::WASMER_OK + // } + // Err(err) => { + // update_last_error(err); + // wasmer_result_t::WASMER_ERROR + // } + // } +} + +/// Gets the memory within the context at the index `memory_idx`. +/// The index is always 0 until multiple memories are supported. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_instance_context_memory( + ctx: *mut wasmer_instance_context_t, + memory_idx: uint32_t, +) -> *const wasmer_memory_t { + let mut ctx = unsafe { &mut *(ctx as *mut Ctx) }; + let memory = ctx.memory(0); + memory as *const Memory as *const wasmer_memory_t +} + +/// Gets the start pointer to the bytes within a Memory +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_memory_data(mem: *mut wasmer_memory_t) -> *mut uint8_t { + let memory = mem as *mut Memory; + use std::cell::Cell; + unsafe { ((*memory).view::()[..]).as_ptr() as *mut Cell as *mut u8 } +} + +/// Gets the size in bytes of a Memory +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_memory_data_length(mem: *mut wasmer_memory_t) -> uint32_t { + let memory = mem as *mut Memory; + let Bytes(len) = unsafe { (*memory).size().bytes() }; + len as uint32_t +} + +/// Frees memory for the given Instance +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_instance_destroy(instance: *mut wasmer_instance_t) { + if !instance.is_null() { + drop(unsafe { Box::from_raw(instance as *mut Instance) }); + } +} + +impl From for Value { + fn from(v: wasmer_value_t) -> Self { + unsafe { + match v { + wasmer_value_t { + tag: WASM_I32, + value: wasmer_value { I32 }, + } => Value::I32(I32), + wasmer_value_t { + tag: WASM_I64, + value: wasmer_value { I64 }, + } => Value::I64(I64), + wasmer_value_t { + tag: WASM_F32, + value: wasmer_value { F32 }, + } => Value::F32(F32), + wasmer_value_t { + tag: WASM_F64, + value: wasmer_value { F64 }, + } => Value::F64(F64), + _ => panic!("not implemented"), + } + } + } +} + +impl From for wasmer_value_t { + fn from(val: Value) -> Self { + match val { + Value::I32(x) => wasmer_value_t { + tag: wasmer_value_tag::WASM_I32, + value: wasmer_value { I32: x }, + }, + Value::I64(x) => wasmer_value_t { + tag: wasmer_value_tag::WASM_I64, + value: wasmer_value { I64: x }, + }, + Value::F32(x) => wasmer_value_t { + tag: wasmer_value_tag::WASM_F32, + value: wasmer_value { F32: x }, + }, + Value::F64(x) => wasmer_value_t { + tag: wasmer_value_tag::WASM_F64, + value: wasmer_value { F64: x }, + }, + } + } +} + +impl From for wasmer_value_tag { + fn from(ty: Type) -> Self { + match ty { + Type::I32 => wasmer_value_tag::WASM_I32, + Type::I64 => wasmer_value_tag::WASM_I64, + Type::F32 => wasmer_value_tag::WASM_F32, + Type::F64 => wasmer_value_tag::WASM_F64, + _ => panic!("not implemented"), + } + } +} + +impl From for Type { + fn from(v: wasmer_value_tag) -> Self { + unsafe { + match v { + wasmer_value_tag::WASM_I32 => Type::I32, + wasmer_value_tag::WASM_I64 => Type::I64, + wasmer_value_tag::WASM_F32 => Type::F32, + wasmer_value_tag::WASM_F64 => Type::F64, + _ => panic!("not implemented"), + } + } + } +} + +impl From<(std::string::String, wasmer_runtime_core::export::Export)> for NamedExport { + fn from((name, export): (String, Export)) -> Self { + NamedExport { name, export } + } +} + +impl From<&wasmer_runtime::wasm::Type> for wasmer_value_tag { + fn from(ty: &Type) -> Self { + match *ty { + Type::I32 => wasmer_value_tag::WASM_I32, + Type::I64 => wasmer_value_tag::WASM_I64, + Type::F32 => wasmer_value_tag::WASM_F32, + Type::F64 => wasmer_value_tag::WASM_F64, + } + } +} + +// Error reporting + +thread_local! { + static LAST_ERROR: RefCell>> = RefCell::new(None); +} + +fn update_last_error(err: E) { + LAST_ERROR.with(|prev| { + *prev.borrow_mut() = Some(Box::new(err)); + }); +} + +/// Retrieve the most recent error, clearing it in the process. +fn take_last_error() -> Option> { + LAST_ERROR.with(|prev| prev.borrow_mut().take()) +} + +/// Gets the length in bytes of the last error. +/// This can be used to dynamically allocate a buffer with the correct number of +/// bytes needed to store a message. +/// +/// # Example +/// ``` +/// int error_len = wasmer_last_error_length(); +/// char *error_str = malloc(error_len); +/// ``` +#[no_mangle] +pub extern "C" fn wasmer_last_error_length() -> c_int { + LAST_ERROR.with(|prev| match *prev.borrow() { + Some(ref err) => err.to_string().len() as c_int + 1, + None => 0, + }) +} + +/// Stores the last error message into the provided buffer up to the given `length`. +/// The `length` parameter must be large enough to store the last error message. +/// +/// Returns the length of the string in bytes. +/// Returns `-1` if an error occurs. +/// +/// # Example +/// ``` +/// int error_len = wasmer_last_error_length(); +/// char *error_str = malloc(error_len); +/// wasmer_last_error_message(error_str, error_len); +/// printf("Error str: `%s`\n", error_str); +/// ``` +#[no_mangle] +pub unsafe extern "C" fn wasmer_last_error_message(buffer: *mut c_char, length: c_int) -> c_int { + if buffer.is_null() { + // buffer pointer is null + return -1; + } + + let last_error = match take_last_error() { + Some(err) => err, + None => return 0, + }; + + let error_message = last_error.to_string(); + + let buffer = slice::from_raw_parts_mut(buffer as *mut u8, length as usize); + + if error_message.len() >= buffer.len() { + // buffer to small for err message + return -1; + } + + ptr::copy_nonoverlapping( + error_message.as_ptr(), + buffer.as_mut_ptr(), + error_message.len(), + ); + + // Add a trailing null so people using the string as a `char *` don't + // accidentally read into garbage. + buffer[error_message.len()] = 0; + + error_message.len() as c_int +} + +#[derive(Debug)] +struct CApiError { + msg: String, +} + +impl fmt::Display for CApiError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", &self.msg) + } +} + +impl Error for CApiError {} + +struct NamedExport { + name: String, + export: Export, +} diff --git a/lib/runtime-c-api/tests/.gitignore b/lib/runtime-c-api/tests/.gitignore new file mode 100644 index 000000000..6ea57c28d --- /dev/null +++ b/lib/runtime-c-api/tests/.gitignore @@ -0,0 +1,20 @@ +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps +test-globals +test-exports +test-instantiate +test-import-function +test-memory +test-module +test-tables +test-validate +rust-build \ No newline at end of file diff --git a/lib/runtime-c-api/tests/CMakeLists.txt b/lib/runtime-c-api/tests/CMakeLists.txt new file mode 100644 index 000000000..788ee1c6a --- /dev/null +++ b/lib/runtime-c-api/tests/CMakeLists.txt @@ -0,0 +1,48 @@ +cmake_minimum_required (VERSION 2.6) +project (WasmerCApiTests) + +add_executable(test-exports test-exports.c) +add_executable(test-globals test-globals.c) +add_executable(test-instantiate test-instantiate.c) +add_executable(test-import-function test-import-function.c) +add_executable(test-memory test-memory.c) +add_executable(test-module test-module.c) +add_executable(test-validate test-validate.c) +add_executable(test-tables test-tables.c) + +find_library( + WASMER_LIB NAMES libwasmer_runtime_c_api.dylib libwasmer_runtime_c_api.so libwasmer_runtime_c_api.dll + PATHS ${CMAKE_SOURCE_DIR}/../../../target/debug/ +) + +if(NOT WASMER_LIB) + message(FATAL_ERROR "wasmer library not found") +endif() + +target_link_libraries(test-exports + general ${WASMER_LIB}) +target_link_libraries(test-globals + general ${WASMER_LIB}) +target_link_libraries(test-instantiate + general ${WASMER_LIB}) +target_link_libraries(test-import-function + general ${WASMER_LIB}) +target_link_libraries(test-memory + general ${WASMER_LIB}) +target_link_libraries(test-module + general ${WASMER_LIB}) +target_link_libraries(test-validate + general ${WASMER_LIB}) +target_link_libraries(test-tables + general ${WASMER_LIB}) + +enable_testing() +add_test(test-exports test-exports) +add_test(test-globals test-globals) +add_test(test-instantiate test-instantiate) +add_test(test-import-function test-import-function) +add_test(test-memory test-memory) +add_test(test-module test-module) +add_test(test-validate test-validate) +add_test(test-tables test-tables) + diff --git a/lib/runtime-c-api/tests/runtime_c_api_tests.rs b/lib/runtime-c-api/tests/runtime_c_api_tests.rs new file mode 100644 index 000000000..b605da49e --- /dev/null +++ b/lib/runtime-c-api/tests/runtime_c_api_tests.rs @@ -0,0 +1,39 @@ +use std::process::Command; + +#[test] +fn test_c_api() { + let project_tests_dir = concat!(env!("CARGO_MANIFEST_DIR"), "/tests"); + run_command("cmake", project_tests_dir, Some(".")); + run_command("make", project_tests_dir, None); + run_command("make", project_tests_dir, Some("test")); +} + +fn run_command(command_str: &str, dir: &str, arg: Option<&str>) { + println!("Running command: `{}` arg: {:?}", command_str, arg); + let mut command = Command::new(command_str); + if let Some(a) = arg { + command.arg(a); + } + command.current_dir(dir); + let result = command.output(); + match result { + Ok(r) => { + println!("output:"); + if let Some(code) = r.status.code() { + println!("status: {}", code); + } else { + println!("status: None"); + } + println!("stdout:"); + println!("{}", String::from_utf8_lossy(&r.stdout[..])); + println!("stderr:"); + println!("{}", String::from_utf8_lossy(&r.stderr[..])); + if r.status.success() { + assert!(true) + } else { + panic!("Command failed with exit status: {:?}", r.status); + } + } + Err(e) => panic!("Command failed: {}", e), + } +} diff --git a/lib/runtime-c-api/tests/sum.wasm b/lib/runtime-c-api/tests/sum.wasm new file mode 100644 index 000000000..135da2604 Binary files /dev/null and b/lib/runtime-c-api/tests/sum.wasm differ diff --git a/lib/runtime-c-api/tests/test-exports.c b/lib/runtime-c-api/tests/test-exports.c new file mode 100644 index 000000000..3f458ebab --- /dev/null +++ b/lib/runtime-c-api/tests/test-exports.c @@ -0,0 +1,86 @@ +#include +#include "../wasmer.h" +#include +#include + +int main() +{ + // Read the wasm file bytes + FILE *file = fopen("sum.wasm", "r"); + 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_import_t imports[] = {}; + wasmer_instance_t *instance = NULL; + wasmer_result_t compile_result = wasmer_instantiate(&instance, bytes, len, imports, 0); + printf("Compile result: %d\n", compile_result); + assert(compile_result == WASMER_OK); + + wasmer_exports_t *exports = NULL; + wasmer_instance_exports(instance, &exports); + + int exports_len = wasmer_exports_len(exports); + printf("exports_len: %d\n", exports_len); + assert(exports_len == 1); + + wasmer_export_t *export = wasmer_exports_get(exports, 0); + + wasmer_import_export_kind kind = wasmer_export_kind(export); + assert(kind == WASM_FUNCTION); + wasmer_func_t *func = wasmer_export_to_func(export); + + wasmer_byte_array name_bytes = wasmer_export_name(export); + assert(name_bytes.bytes_len == 3); + char expected[] = {'s', 'u', 'm'}; + for(int idx = 0; idx < 3; idx++){ + printf("%c\n", name_bytes.bytes[idx]); + assert(name_bytes.bytes[idx] == expected[idx]); + } + + uint32_t params_arity; + wasmer_func_params_arity(func, ¶ms_arity); + assert(params_arity == 2); + + wasmer_value_tag *params_sig = malloc(sizeof(wasmer_value_tag) * params_arity); + wasmer_func_params(func, params_sig , params_arity); + assert(params_sig[0] == WASM_I32); + assert(params_sig[1] == WASM_I32); + free(params_sig); + + uint32_t returns_arity; + wasmer_func_returns_arity(func, &returns_arity); + assert(returns_arity == 1); + + wasmer_value_tag *returns_sig = malloc(sizeof(wasmer_value_tag) * returns_arity); + wasmer_func_returns(func, returns_sig , returns_arity); + assert(returns_sig[0] == WASM_I32); + free(returns_sig); + + +// wasmer_value_t param_one; +// param_one.tag = WASM_I32; +// param_one.value.I32 = 7; +// wasmer_value_t param_two; +// param_two.tag = WASM_I32; +// param_two.value.I32 = 8; +// wasmer_value_t params[] = {param_one, param_two}; +// wasmer_value_t result_one; +// wasmer_value_t results[] = {result_one}; +// +// wasmer_result_t call_result = wasmer_func_call(func, params, 2, results, 1); +// printf("Call result: %d\n", call_result); +// printf("Result: %d\n", results[0].value.I32); +// assert(results[0].value.I32 == 15); +// assert(call_result == WASMER_OK); + + + printf("Destroy instance\n"); + wasmer_instance_destroy(instance); + printf("Destroy exports\n"); + wasmer_exports_destroy(exports); + return 0; +} \ No newline at end of file diff --git a/lib/runtime-c-api/tests/test-globals.c b/lib/runtime-c-api/tests/test-globals.c new file mode 100644 index 000000000..a694d2e75 --- /dev/null +++ b/lib/runtime-c-api/tests/test-globals.c @@ -0,0 +1,30 @@ +#include +#include "../wasmer.h" +#include +#include + +int main() +{ + wasmer_value_t val; + val.tag = WASM_I32; + val.value.I32 = 7; + wasmer_global_t *global = wasmer_global_new(val, true); + + wasmer_value_t get_val = wasmer_global_get(global); + assert( get_val.value.I32 == 7); + + wasmer_value_t val2; + val2.tag = WASM_I32; + val2.value.I32 = 14; + wasmer_global_set(global, val2); + + wasmer_value_t new_get_val = wasmer_global_get(global); + assert( new_get_val.value.I32 == 14); + + wasmer_global_descriptor_t desc = wasmer_global_get_descriptor(global); + assert(desc.mutable_); + assert(desc.kind == WASM_I32); + + wasmer_global_destroy(global); + return 0; +} diff --git a/lib/runtime-c-api/tests/test-import-function.c b/lib/runtime-c-api/tests/test-import-function.c new file mode 100644 index 000000000..e7caed874 --- /dev/null +++ b/lib/runtime-c-api/tests/test-import-function.c @@ -0,0 +1,91 @@ +#include +#include "../wasmer.h" +#include +#include +#include + +static print_str_called = false; +static memory_len = 0; +static ptr_len = 0; +static char actual_str[14] = {}; + +void print_str(wasmer_instance_context_t *ctx, int32_t ptr, int32_t len) +{ + 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); + for (int32_t idx = 0; idx < len; idx++) + { + actual_str[idx] = mem_bytes[ptr + idx]; + } + actual_str[13] = '\0'; + printf("In print_str, memory len: %d, ptr_len: %d\n, str %s", mem_len, len, actual_str); + print_str_called = true; + memory_len = mem_len; + ptr_len = len; +} + +int main() +{ + wasmer_value_tag params_sig[] = {WASM_I32, WASM_I32}; + wasmer_value_tag returns_sig[] = {}; + + printf("Creating new func\n"); + wasmer_func_t *func = wasmer_func_new(print_str, params_sig, 2, returns_sig, 0); + wasmer_import_t import; + + char *module_name = "env"; + wasmer_byte_array module_name_bytes; + module_name_bytes.bytes = module_name; + module_name_bytes.bytes_len = strlen(module_name); + char *import_name = "print_str"; + wasmer_byte_array import_name_bytes; + import_name_bytes.bytes = import_name; + import_name_bytes.bytes_len = strlen(import_name); + + import.module_name = module_name_bytes; + import.import_name = import_name_bytes; + import.tag = WASM_FUNCTION; + import.value.func = func; + wasmer_import_t imports[] = {import}; + + // Read the wasm file bytes + FILE *file = fopen("wasm_sample_app.wasm", "r"); + 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); + + printf("Instantiating\n"); + wasmer_instance_t *instance = NULL; + wasmer_result_t compile_result = wasmer_instantiate(&instance, bytes, len, imports, 1); + printf("Compile result: %d\n", compile_result); + + assert(compile_result == WASMER_OK); + + wasmer_value_t params[] = {}; + wasmer_value_t results[] = {}; + wasmer_result_t call_result = wasmer_instance_call(instance, "hello_wasm", params, 0, results, 0); + printf("Call result: %d\n", call_result); + + 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); + + assert(call_result == WASMER_OK); + + assert(print_str_called); + assert(memory_len == 17); + assert(ptr_len == 13); + assert(0 == strcmp(actual_str, "Hello, World!")); + + printf("Destroying func\n"); + wasmer_func_destroy(func); + printf("Destroy instance\n"); + wasmer_instance_destroy(instance); + return 0; +} \ No newline at end of file diff --git a/lib/runtime-c-api/tests/test-instantiate.c b/lib/runtime-c-api/tests/test-instantiate.c new file mode 100644 index 000000000..2a675de12 --- /dev/null +++ b/lib/runtime-c-api/tests/test-instantiate.c @@ -0,0 +1,56 @@ +#include +#include "../wasmer.h" +#include +#include + +int main() +{ + // Read the wasm file bytes + FILE *file = fopen("sum.wasm", "r"); + 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_import_t imports[] = {}; + wasmer_instance_t *instance = NULL; + wasmer_result_t compile_result = wasmer_instantiate(&instance, bytes, len, imports, 0); + printf("Compile result: %d\n", compile_result); + assert(compile_result == WASMER_OK); + + wasmer_value_t param_one; + param_one.tag = WASM_I32; + param_one.value.I32 = 7; + wasmer_value_t param_two; + param_two.tag = WASM_I32; + param_two.value.I32 = 8; + wasmer_value_t params[] = {param_one, param_two}; + + wasmer_value_t result_one; + wasmer_value_t results[] = {result_one}; + + wasmer_result_t call_result = wasmer_instance_call(instance, "sum", params, 2, results, 1); + printf("Call result: %d\n", call_result); + printf("Result: %d\n", results[0].value.I32); + assert(results[0].value.I32 == 15); + assert(call_result == WASMER_OK); + + + wasmer_result_t call_result2 = wasmer_instance_call(instance, "sum", params, 1, results, 1); + printf("Call result bad: %d\n", call_result2); + assert(call_result2 == 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); + assert(0 == strcmp(error_str, "Call error: Parameters of type [I32] did not match signature [I32, I32] -> [I32]")); + free(error_str); + + printf("Destroy instance\n"); + wasmer_instance_destroy(instance); + return 0; +} \ No newline at end of file diff --git a/lib/runtime-c-api/tests/test-memory.c b/lib/runtime-c-api/tests/test-memory.c new file mode 100644 index 000000000..63913c73b --- /dev/null +++ b/lib/runtime-c-api/tests/test-memory.c @@ -0,0 +1,62 @@ +#include +#include "../wasmer.h" +#include +#include + +int main() +{ + wasmer_memory_t *memory = NULL; + wasmer_limits_t descriptor; + descriptor.min = 10; + wasmer_limit_option_t max; + max.has_some = true; + max.some = 15; + descriptor.max = max; + wasmer_result_t memory_result = wasmer_memory_new(&memory, descriptor); + printf("Memory result: %d\n", memory_result); + assert(memory_result == WASMER_OK); + + uint32_t len = wasmer_memory_length(memory); + printf("Memory pages length: %d\n", len); + assert(len == 10); + + wasmer_result_t grow_result = wasmer_memory_grow(memory, 2); + assert(grow_result == WASMER_OK); + + uint32_t new_len = wasmer_memory_length(memory); + printf("Memory pages length: %d\n", new_len); + assert(new_len == 12); + + uint32_t bytes_len = wasmer_memory_data_length(memory); + printf("Memory bytes length: %d\n", bytes_len); + assert(bytes_len == 12 * 65536); + + // Err, grow beyond max + wasmer_result_t grow_result2 = wasmer_memory_grow(memory, 10); + assert(grow_result2 == WASMER_ERROR); + int error_len = wasmer_last_error_length(); + char *error_str = malloc(error_len); + wasmer_last_error_message(error_str, error_len); + printf("Error str: `%s`\n", error_str); + assert(0 == strcmp(error_str, "unable to grow memory")); + free(error_str); + + +// wasmer_memory_t *bad_memory = NULL; +// wasmer_limits_t bad_descriptor; +// bad_descriptor.min = 15; +// bad_descriptor.max = 10; +// wasmer_result_t bad_memory_result = wasmer_memory_new(&bad_memory, bad_descriptor); +// printf("Bad memory result: %d\n", bad_memory_result); +// assert(memory_result == WASMER_MEMORY_ERROR); +// +// int error_len = wasmer_last_error_length(); +// char *error_str = malloc(error_len); +// wasmer_last_error_message(error_str, error_len); +// assert(0 == strcmp(error_str, "Creation error")); +// free(error_str); + + printf("Destroy memory\n"); + wasmer_memory_destroy(memory); + return 0; +} diff --git a/lib/runtime-c-api/tests/test-module.c b/lib/runtime-c-api/tests/test-module.c new file mode 100644 index 000000000..bacd37c3a --- /dev/null +++ b/lib/runtime-c-api/tests/test-module.c @@ -0,0 +1,25 @@ +#include +#include "../wasmer.h" +#include +#include + +int main() +{ + // Read the wasm file bytes + FILE *file = fopen("sum.wasm", "r"); + 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; + wasmer_result_t compile_result = wasmer_compile(&module, bytes, len); + printf("Compile result: %d\n", compile_result); + assert(compile_result == WASMER_OK); + + printf("Destroy module\n"); + wasmer_module_destroy(module); + return 0; +} \ No newline at end of file diff --git a/lib/runtime-c-api/tests/test-tables.c b/lib/runtime-c-api/tests/test-tables.c new file mode 100644 index 000000000..66ebf832d --- /dev/null +++ b/lib/runtime-c-api/tests/test-tables.c @@ -0,0 +1,48 @@ +#include +#include "../wasmer.h" +#include +#include + +int main() +{ + wasmer_table_t *table = NULL; + wasmer_limits_t descriptor; + descriptor.min = 10; + wasmer_limit_option_t max; +// max.has_some = false; + max.has_some = true; + max.some = 15; + descriptor.max = max; + wasmer_result_t table_result = wasmer_table_new(&table, descriptor); + printf("Table result: %d\n", table_result); + assert(table_result == WASMER_OK); + + uint32_t len = wasmer_table_length(table); + printf("Table length: %d\n", len); + assert(len == 15); + +// wasmer_result_t grow_result1 = wasmer_table_grow(table, 5); +// assert(grow_result1 == WASMER_OK); +// uint32_t len_grow1 = wasmer_table_length(table); +// printf("Table length: %d\n", len_grow1); +// assert(len_grow1 == 15); + + // // Try to grow beyond max + // wasmer_result_t grow_result2 = wasmer_table_grow(&table, 1); + // assert(grow_result2 == WASMER_ERROR); + // uint32_t len_grow2 = wasmer_table_length(table); + // printf("Table length: %d\n", len_grow2); + // assert(len_grow2 == 15); + +// wasmer_table_t *table_bad = NULL; +// wasmer_limits_t bad_descriptor; +// bad_descriptor.min = 15; +// bad_descriptor.max = 10; +// wasmer_result_t table_bad_result = wasmer_table_new(&table_bad, bad_descriptor); +// printf("Table result: %d\n", table_bad_result); +// assert(table_result == WASMER_ERROR); + + printf("Destroy table\n"); + wasmer_table_destroy(table); + return 0; +} diff --git a/lib/runtime-c-api/tests/test-validate.c b/lib/runtime-c-api/tests/test-validate.c new file mode 100644 index 000000000..689cf50f2 --- /dev/null +++ b/lib/runtime-c-api/tests/test-validate.c @@ -0,0 +1,22 @@ +#include +#include "../wasmer.h" +#include +#include + +int main() +{ + // Read the wasm file bytes + FILE *file = fopen("sum.wasm", "r"); + 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); + + bool result = wasmer_validate(bytes, len); + printf("Result: %d", result); + assert(result); + + return 0; +} diff --git a/lib/runtime-c-api/tests/wasm_sample_app.wasm b/lib/runtime-c-api/tests/wasm_sample_app.wasm new file mode 100755 index 000000000..7c8c4b72a Binary files /dev/null and b/lib/runtime-c-api/tests/wasm_sample_app.wasm differ diff --git a/lib/runtime-c-api/wasmer.h b/lib/runtime-c-api/wasmer.h new file mode 100644 index 000000000..8d328836f --- /dev/null +++ b/lib/runtime-c-api/wasmer.h @@ -0,0 +1,381 @@ +#ifndef WASMER_H +#define WASMER_H + +#include +#include +#include +#include + +enum wasmer_import_export_kind { + WASM_FUNCTION, + WASM_GLOBAL, + WASM_MEMORY, + WASM_TABLE, +}; +typedef uint32_t wasmer_import_export_kind; + +typedef enum { + WASMER_OK = 1, + WASMER_ERROR = 2, +} wasmer_result_t; + +enum wasmer_value_tag { + WASM_I32, + WASM_I64, + WASM_F32, + WASM_F64, +}; +typedef uint32_t wasmer_value_tag; + +typedef struct wasmer_instance_context_t wasmer_instance_context_t; + +typedef struct wasmer_instance_t wasmer_instance_t; + +typedef struct wasmer_module_t wasmer_module_t; + +typedef struct { + +} wasmer_export_t; + +typedef struct { + const uint8_t *bytes; + uint32_t bytes_len; +} wasmer_byte_array; + +typedef struct { + +} wasmer_func_t; + +typedef struct { + +} wasmer_exports_t; + +typedef union { + int32_t I32; + int64_t I64; + float F32; + double F64; +} wasmer_value; + +typedef struct { + wasmer_value_tag tag; + wasmer_value value; +} wasmer_value_t; + +typedef struct { + +} wasmer_global_t; + +typedef struct { + bool mutable_; + wasmer_value_tag kind; +} wasmer_global_descriptor_t; + +typedef struct { + +} wasmer_memory_t; + +typedef struct { + +} wasmer_table_t; + +typedef union { + const wasmer_func_t *func; + const wasmer_table_t *table; + const wasmer_memory_t *memory; + const wasmer_global_t *global; +} wasmer_import_export_value; + +typedef struct { + wasmer_byte_array module_name; + wasmer_byte_array import_name; + wasmer_import_export_kind tag; + wasmer_import_export_value value; +} wasmer_import_t; + +typedef struct { + bool has_some; + uint32_t some; +} wasmer_limit_option_t; + +typedef struct { + uint32_t min; + wasmer_limit_option_t max; +} wasmer_limits_t; + +/** + * Creates a new Module from the given wasm bytes. + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_compile(wasmer_module_t **module, + uint8_t *wasm_bytes, + uint32_t wasm_bytes_len); + +/** + * Gets wasmer_export kind + */ +wasmer_import_export_kind wasmer_export_kind(wasmer_export_t *export_); + +/** + * Gets name from wasmer_export + */ +wasmer_byte_array wasmer_export_name(wasmer_export_t *export_); + +/** + * Gets func from wasm_export + */ +const wasmer_func_t *wasmer_export_to_func(wasmer_export_t *export_); + +/** + * Frees the memory for the given exports + */ +void wasmer_exports_destroy(wasmer_exports_t *exports); + +/** + * Gets wasmer_export by index + */ +wasmer_export_t *wasmer_exports_get(wasmer_exports_t *exports, int idx); + +/** + * Gets the length of the exports + */ +int wasmer_exports_len(wasmer_exports_t *exports); + +/** + * Calls a `func` with the provided parameters. + * Results are set using the provided `results` pointer. + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_func_call(wasmer_func_t *func, + const wasmer_value_t *params, + int params_len, + wasmer_value_t *results, + int results_len); + +/** + * Frees memory for the given Func + */ +void wasmer_func_destroy(wasmer_func_t *func); + +/** + * Creates new func + * The caller owns the object and should call `wasmer_func_destroy` to free it. + */ +const wasmer_func_t *wasmer_func_new(void (*func)(void *data), + const wasmer_value_tag *params, + int params_len, + const wasmer_value_tag *returns, + int returns_len); + +/** + * Sets the params buffer to the parameter types of the given wasmer_func_t + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_func_params(wasmer_func_t *func, wasmer_value_tag *params, int params_len); + +/** + * Sets the result parameter to the arity of the params of the wasmer_func_t + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_func_params_arity(wasmer_func_t *func, uint32_t *result); + +/** + * Sets the returns buffer to the parameter types of the given wasmer_func_t + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_func_returns(wasmer_func_t *func, + wasmer_value_tag *returns, + int returns_len); + +/** + * Sets the result parameter to the arity of the returns of the wasmer_func_t + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_func_returns_arity(wasmer_func_t *func, uint32_t *result); + +/** + * Frees memory for the given Global + */ +void wasmer_global_destroy(wasmer_global_t *global); + +/** + * Gets the value stored by the given Global + */ +wasmer_value_t wasmer_global_get(wasmer_global_t *global); + +/** + * Returns a descriptor (type, mutability) of the given Global + */ +wasmer_global_descriptor_t wasmer_global_get_descriptor(wasmer_global_t *global); + +/** + * Creates a new Global and returns a pointer to it. + * The caller owns the object and should call `wasmer_global_destroy` to free it. + */ +wasmer_global_t *wasmer_global_new(wasmer_value_t value, bool mutable_); + +/** + * Sets the value stored by the given Global + */ +void wasmer_global_set(wasmer_global_t *global, wasmer_value_t value); + +/** + * Calls an instances exported function by `name` with the provided parameters. + * Results are set using the provided `results` pointer. + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_instance_call(wasmer_instance_t *instance, + const char *name, + const wasmer_value_t *params, + int params_len, + wasmer_value_t *results, + int results_len); + +/** + * Gets the memory within the context at the index `memory_idx`. + * The index is always 0 until multiple memories are supported. + */ +const wasmer_memory_t *wasmer_instance_context_memory(wasmer_instance_context_t *ctx, + uint32_t memory_idx); + +/** + * Frees memory for the given Instance + */ +void wasmer_instance_destroy(wasmer_instance_t *instance); + +/** + * Gets Exports for the given instance + * The caller owns the object and should call `wasmer_exports_destroy` to free it. + */ +void wasmer_instance_exports(wasmer_instance_t *instance, wasmer_exports_t **exports); + +/** + * Creates a new Instance from the given wasm bytes and imports. + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_instantiate(wasmer_instance_t **instance, + uint8_t *wasm_bytes, + uint32_t wasm_bytes_len, + wasmer_import_t *imports, + int imports_len); + +/** + * Gets the length in bytes of the last error. + * This can be used to dynamically allocate a buffer with the correct number of + * bytes needed to store a message. + * # Example + * ``` + * int error_len = wasmer_last_error_length(); + * char *error_str = malloc(error_len); + * ``` + */ +int wasmer_last_error_length(void); + +/** + * Stores the last error message into the provided buffer up to the given `length`. + * The `length` parameter must be large enough to store the last error message. + * Returns the length of the string in bytes. + * Returns `-1` if an error occurs. + * # Example + * ``` + * int error_len = wasmer_last_error_length(); + * char *error_str = malloc(error_len); + * wasmer_last_error_message(error_str, error_len); + * printf("Error str: `%s`\n", error_str); + * ``` + */ +int wasmer_last_error_message(char *buffer, int length); + +/** + * Gets the start pointer to the bytes within a Memory + */ +uint8_t *wasmer_memory_data(wasmer_memory_t *mem); + +/** + * Gets the size in bytes of a Memory + */ +uint32_t wasmer_memory_data_length(wasmer_memory_t *mem); + +/** + * Frees memory for the given Memory + */ +void wasmer_memory_destroy(wasmer_memory_t *memory); + +/** + * Grows a Memory by the given number of pages. + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_memory_grow(wasmer_memory_t *memory, uint32_t delta); + +/** + * Returns the current length in pages of the given memory + */ +uint32_t wasmer_memory_length(wasmer_memory_t *memory); + +/** + * Creates a new Memory for the given descriptor and initializes the given + * pointer to pointer to a pointer to the new memory. + * The caller owns the object and should call `wasmer_memory_destroy` to free it. + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_memory_new(wasmer_memory_t **memory, wasmer_limits_t limits); + +/** + * Frees memory for the given Module + */ +void wasmer_module_destroy(wasmer_module_t *module); + +/** + * Frees memory for the given Table + */ +void wasmer_table_destroy(wasmer_table_t *table); + +/** + * Grows a Table by the given number of elements. + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_table_grow(wasmer_table_t *table, uint32_t delta); + +/** + * Returns the current length of the given Table + */ +uint32_t wasmer_table_length(wasmer_table_t *table); + +/** + * Creates a new Table for the given descriptor and initializes the given + * pointer to pointer to a pointer to the new Table. + * The caller owns the object and should call `wasmer_table_destroy` to free it. + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_table_new(wasmer_table_t **table, wasmer_limits_t limits); + +/** + * Returns true for valid wasm bytes and false for invalid bytes + */ +bool wasmer_validate(uint8_t *wasm_bytes, uint32_t wasm_bytes_len); + +#endif /* WASMER_H */ diff --git a/lib/runtime-c-api/wasmer.hh b/lib/runtime-c-api/wasmer.hh new file mode 100644 index 000000000..ab2808bf3 --- /dev/null +++ b/lib/runtime-c-api/wasmer.hh @@ -0,0 +1,306 @@ +#ifndef WASMER_H +#define WASMER_H + +#include +#include +#include + +enum class wasmer_import_export_kind : uint32_t { + WASM_FUNCTION, + WASM_GLOBAL, + WASM_MEMORY, + WASM_TABLE, +}; + +enum class wasmer_result_t { + WASMER_OK = 1, + WASMER_ERROR = 2, +}; + +enum class wasmer_value_tag : uint32_t { + WASM_I32, + WASM_I64, + WASM_F32, + WASM_F64, +}; + +struct wasmer_instance_context_t; + +struct wasmer_instance_t; + +struct wasmer_module_t; + +struct wasmer_export_t { + +}; + +struct wasmer_byte_array { + const uint8_t *bytes; + uint32_t bytes_len; +}; + +struct wasmer_func_t { + +}; + +struct wasmer_exports_t { + +}; + +union wasmer_value { + int32_t I32; + int64_t I64; + float F32; + double F64; +}; + +struct wasmer_value_t { + wasmer_value_tag tag; + wasmer_value value; +}; + +struct wasmer_global_t { + +}; + +struct wasmer_global_descriptor_t { + bool mutable_; + wasmer_value_tag kind; +}; + +struct wasmer_memory_t { + +}; + +struct wasmer_table_t { + +}; + +union wasmer_import_export_value { + const wasmer_func_t *func; + const wasmer_table_t *table; + const wasmer_memory_t *memory; + const wasmer_global_t *global; +}; + +struct wasmer_import_t { + wasmer_byte_array module_name; + wasmer_byte_array import_name; + wasmer_import_export_kind tag; + wasmer_import_export_value value; +}; + +struct wasmer_limit_option_t { + bool has_some; + uint32_t some; +}; + +struct wasmer_limits_t { + uint32_t min; + wasmer_limit_option_t max; +}; + +extern "C" { + +/// Creates a new Module from the given wasm bytes. +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_compile(wasmer_module_t **module, + uint8_t *wasm_bytes, + uint32_t wasm_bytes_len); + +/// Gets wasmer_export kind +wasmer_import_export_kind wasmer_export_kind(wasmer_export_t *export_); + +/// Gets name from wasmer_export +wasmer_byte_array wasmer_export_name(wasmer_export_t *export_); + +/// Gets func from wasm_export +const wasmer_func_t *wasmer_export_to_func(wasmer_export_t *export_); + +/// Frees the memory for the given exports +void wasmer_exports_destroy(wasmer_exports_t *exports); + +/// Gets wasmer_export by index +wasmer_export_t *wasmer_exports_get(wasmer_exports_t *exports, int idx); + +/// Gets the length of the exports +int wasmer_exports_len(wasmer_exports_t *exports); + +/// Calls a `func` with the provided parameters. +/// Results are set using the provided `results` pointer. +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_func_call(wasmer_func_t *func, + const wasmer_value_t *params, + int params_len, + wasmer_value_t *results, + int results_len); + +/// Frees memory for the given Func +void wasmer_func_destroy(wasmer_func_t *func); + +/// Creates new func +/// The caller owns the object and should call `wasmer_func_destroy` to free it. +const wasmer_func_t *wasmer_func_new(void (*func)(void *data), + const wasmer_value_tag *params, + int params_len, + const wasmer_value_tag *returns, + int returns_len); + +/// Sets the params buffer to the parameter types of the given wasmer_func_t +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_func_params(wasmer_func_t *func, wasmer_value_tag *params, int params_len); + +/// Sets the result parameter to the arity of the params of the wasmer_func_t +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_func_params_arity(wasmer_func_t *func, uint32_t *result); + +/// Sets the returns buffer to the parameter types of the given wasmer_func_t +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_func_returns(wasmer_func_t *func, + wasmer_value_tag *returns, + int returns_len); + +/// Sets the result parameter to the arity of the returns of the wasmer_func_t +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_func_returns_arity(wasmer_func_t *func, uint32_t *result); + +/// Frees memory for the given Global +void wasmer_global_destroy(wasmer_global_t *global); + +/// Gets the value stored by the given Global +wasmer_value_t wasmer_global_get(wasmer_global_t *global); + +/// Returns a descriptor (type, mutability) of the given Global +wasmer_global_descriptor_t wasmer_global_get_descriptor(wasmer_global_t *global); + +/// Creates a new Global and returns a pointer to it. +/// The caller owns the object and should call `wasmer_global_destroy` to free it. +wasmer_global_t *wasmer_global_new(wasmer_value_t value, bool mutable_); + +/// Sets the value stored by the given Global +void wasmer_global_set(wasmer_global_t *global, wasmer_value_t value); + +/// Calls an instances exported function by `name` with the provided parameters. +/// Results are set using the provided `results` pointer. +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_instance_call(wasmer_instance_t *instance, + const char *name, + const wasmer_value_t *params, + int params_len, + wasmer_value_t *results, + int results_len); + +/// Gets the memory within the context at the index `memory_idx`. +/// The index is always 0 until multiple memories are supported. +const wasmer_memory_t *wasmer_instance_context_memory(wasmer_instance_context_t *ctx, + uint32_t memory_idx); + +/// Frees memory for the given Instance +void wasmer_instance_destroy(wasmer_instance_t *instance); + +/// Gets Exports for the given instance +/// The caller owns the object and should call `wasmer_exports_destroy` to free it. +void wasmer_instance_exports(wasmer_instance_t *instance, wasmer_exports_t **exports); + +/// Creates a new Instance from the given wasm bytes and imports. +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_instantiate(wasmer_instance_t **instance, + uint8_t *wasm_bytes, + uint32_t wasm_bytes_len, + wasmer_import_t *imports, + int imports_len); + +/// Gets the length in bytes of the last error. +/// This can be used to dynamically allocate a buffer with the correct number of +/// bytes needed to store a message. +/// # Example +/// ``` +/// int error_len = wasmer_last_error_length(); +/// char *error_str = malloc(error_len); +/// ``` +int wasmer_last_error_length(); + +/// Stores the last error message into the provided buffer up to the given `length`. +/// The `length` parameter must be large enough to store the last error message. +/// Returns the length of the string in bytes. +/// Returns `-1` if an error occurs. +/// # Example +/// ``` +/// int error_len = wasmer_last_error_length(); +/// char *error_str = malloc(error_len); +/// wasmer_last_error_message(error_str, error_len); +/// printf("Error str: `%s`\n", error_str); +/// ``` +int wasmer_last_error_message(char *buffer, int length); + +/// Gets the start pointer to the bytes within a Memory +uint8_t *wasmer_memory_data(wasmer_memory_t *mem); + +/// Gets the size in bytes of a Memory +uint32_t wasmer_memory_data_length(wasmer_memory_t *mem); + +/// Frees memory for the given Memory +void wasmer_memory_destroy(wasmer_memory_t *memory); + +/// Grows a Memory by the given number of pages. +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_memory_grow(wasmer_memory_t *memory, uint32_t delta); + +/// Returns the current length in pages of the given memory +uint32_t wasmer_memory_length(wasmer_memory_t *memory); + +/// Creates a new Memory for the given descriptor and initializes the given +/// pointer to pointer to a pointer to the new memory. +/// The caller owns the object and should call `wasmer_memory_destroy` to free it. +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_memory_new(wasmer_memory_t **memory, wasmer_limits_t limits); + +/// Frees memory for the given Module +void wasmer_module_destroy(wasmer_module_t *module); + +/// Frees memory for the given Table +void wasmer_table_destroy(wasmer_table_t *table); + +/// Grows a Table by the given number of elements. +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_table_grow(wasmer_table_t *table, uint32_t delta); + +/// Returns the current length of the given Table +uint32_t wasmer_table_length(wasmer_table_t *table); + +/// Creates a new Table for the given descriptor and initializes the given +/// pointer to pointer to a pointer to the new Table. +/// The caller owns the object and should call `wasmer_table_destroy` to free it. +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_table_new(wasmer_table_t **table, wasmer_limits_t limits); + +/// Returns true for valid wasm bytes and false for invalid bytes +bool wasmer_validate(uint8_t *wasm_bytes, uint32_t wasm_bytes_len); + +} // extern "C" + +#endif // WASMER_H diff --git a/lib/runtime-core/src/error.rs b/lib/runtime-core/src/error.rs index e88a5692c..33c9fd15f 100644 --- a/lib/runtime-core/src/error.rs +++ b/lib/runtime-core/src/error.rs @@ -260,6 +260,8 @@ impl std::fmt::Display for CallError { } } +impl std::error::Error for CallError {} + /// This error type is produced when creating something, /// like a `Memory` or a `Table`. #[derive(Debug, Clone)]