mirror of
https://github.com/fluencelabs/wasmer
synced 2025-03-16 16:20:49 +00:00
Merge #856
856: Add WASI support to C API r=MarkMcCaskey a=MarkMcCaskey This is an additive change (with one unrelated clean up of `*mut ptr -> *const ptr`). It exposes the functions to get a WASI import object. It also implements a function on import object to get an `import` with namespace and name. These changes should be okay to ship now, we can follow up to finish adding methods to ImportObject on the C API side and start migrating away from `*imports`. # Review - [x] Add a short description of the the change to the CHANGELOG.md file Co-authored-by: Mark McCaskey <mark@wasmer.io> Co-authored-by: Ivan Enderlin <ivan.enderlin@hoa-project.net> Co-authored-by: Mark McCaskey <markmccaskey@users.noreply.github.com> Co-authored-by: nlewycky <nick@wasmer.io>
This commit is contained in:
commit
2dbe80ad34
@ -3,6 +3,7 @@
|
||||
## **[Unreleased]**
|
||||
|
||||
- [#883](https://github.com/wasmerio/wasmer/pull/883) Allow floating point operations to have arbitrary inputs, even including SNaNs.
|
||||
- [#856](https://github.com/wasmerio/wasmer/pull/856) Expose methods in the runtime C API to get a WASI import object
|
||||
|
||||
## 0.9.0 - 2019-10-23
|
||||
|
||||
|
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1508,6 +1508,7 @@ dependencies = [
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasmer-runtime 0.9.0",
|
||||
"wasmer-runtime-core 0.9.0",
|
||||
"wasmer-wasi 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
2
Makefile
2
Makefile
@ -105,6 +105,8 @@ capi:
|
||||
test-capi: capi
|
||||
cargo test -p wasmer-runtime-c-api --release
|
||||
|
||||
capi-test: test-capi
|
||||
|
||||
test-rest:
|
||||
cargo test --release --all --exclude wasmer-runtime-c-api --exclude wasmer-emscripten --exclude wasmer-spectests --exclude wasmer-wasi --exclude wasmer-middleware-common --exclude wasmer-middleware-common-tests --exclude wasmer-singlepass-backend --exclude wasmer-clif-backend --exclude wasmer-llvm-backend --exclude wasmer-wasi-tests --exclude wasmer-emscripten-tests
|
||||
|
||||
|
@ -24,12 +24,19 @@ default-features = false
|
||||
path = "../runtime-core"
|
||||
version = "0.9.0"
|
||||
|
||||
[dependencies.wasmer-wasi]
|
||||
default-features = false
|
||||
path = "../wasi"
|
||||
version = "0.9.0"
|
||||
optional = true
|
||||
|
||||
[features]
|
||||
default = ["cranelift-backend"]
|
||||
default = ["cranelift-backend", "wasi"]
|
||||
debug = ["wasmer-runtime/debug"]
|
||||
cranelift-backend = ["wasmer-runtime/cranelift", "wasmer-runtime/default-backend-cranelift"]
|
||||
llvm-backend = ["wasmer-runtime/llvm", "wasmer-runtime/default-backend-llvm"]
|
||||
singlepass-backend = ["wasmer-runtime/singlepass", "wasmer-runtime/default-backend-singlepass"]
|
||||
wasi = ["wasmer-wasi"]
|
||||
|
||||
[build-dependencies]
|
||||
cbindgen = "0.9"
|
||||
|
@ -85,12 +85,41 @@ pub union wasmer_import_export_value {
|
||||
/// List of export/import kinds.
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(u32)]
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
// ================
|
||||
// ! DANGER !
|
||||
// ================
|
||||
// Do not modify these values without updating the `TryFrom` implementation below
|
||||
pub enum wasmer_import_export_kind {
|
||||
WASM_FUNCTION,
|
||||
WASM_GLOBAL,
|
||||
WASM_MEMORY,
|
||||
WASM_TABLE,
|
||||
WASM_FUNCTION = 0,
|
||||
WASM_GLOBAL = 1,
|
||||
WASM_MEMORY = 2,
|
||||
WASM_TABLE = 3,
|
||||
}
|
||||
|
||||
impl wasmer_import_export_kind {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::WASM_FUNCTION => "function",
|
||||
Self::WASM_GLOBAL => "global",
|
||||
Self::WASM_MEMORY => "memory",
|
||||
Self::WASM_TABLE => "table",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<u32> for wasmer_import_export_kind {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
0 => Self::WASM_FUNCTION,
|
||||
1 => Self::WASM_GLOBAL,
|
||||
2 => Self::WASM_MEMORY,
|
||||
3 => Self::WASM_TABLE,
|
||||
_ => return Err(()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets export descriptors for the given module
|
||||
|
@ -9,11 +9,11 @@ use crate::{
|
||||
wasmer_byte_array, wasmer_result_t,
|
||||
};
|
||||
use libc::c_uint;
|
||||
use std::{ffi::c_void, ptr, slice, sync::Arc};
|
||||
use std::{convert::TryFrom, ffi::c_void, ptr, slice, sync::Arc};
|
||||
use wasmer_runtime::{Global, Memory, Module, Table};
|
||||
use wasmer_runtime_core::{
|
||||
export::{Context, Export, FuncPointer},
|
||||
import::ImportObject,
|
||||
import::{ImportObject, ImportObjectIterator},
|
||||
module::ImportName,
|
||||
types::{FuncSig, Type},
|
||||
};
|
||||
@ -41,6 +41,10 @@ pub struct wasmer_import_descriptor_t;
|
||||
#[derive(Clone)]
|
||||
pub struct wasmer_import_descriptors_t;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone)]
|
||||
pub struct wasmer_import_object_iter_t;
|
||||
|
||||
/// Creates a new empty import object.
|
||||
/// See also `wasmer_import_object_append`
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
@ -51,12 +55,318 @@ pub unsafe extern "C" fn wasmer_import_object_new() -> *mut wasmer_import_object
|
||||
Box::into_raw(import_object) as *mut wasmer_import_object_t
|
||||
}
|
||||
|
||||
#[cfg(feature = "wasi")]
|
||||
mod wasi;
|
||||
|
||||
#[cfg(feature = "wasi")]
|
||||
pub use self::wasi::*;
|
||||
|
||||
/// 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`.
|
||||
///
|
||||
/// The caller owns all data involved.
|
||||
/// `import_export_value` will be written to based on `tag`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_import_object_get_import(
|
||||
import_object: *const wasmer_import_object_t,
|
||||
namespace: wasmer_byte_array,
|
||||
name: wasmer_byte_array,
|
||||
import: *mut wasmer_import_t,
|
||||
import_export_value: *mut wasmer_import_export_value,
|
||||
tag: u32,
|
||||
) -> wasmer_result_t {
|
||||
let tag: wasmer_import_export_kind = if let Ok(t) = TryFrom::try_from(tag) {
|
||||
t
|
||||
} else {
|
||||
update_last_error(CApiError {
|
||||
msg: "wasmer_import_export_tag out of range".to_string(),
|
||||
});
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
};
|
||||
let import_object: &mut ImportObject = &mut *(import_object as *mut ImportObject);
|
||||
let namespace_str = if let Ok(ns) = namespace.as_str() {
|
||||
ns
|
||||
} else {
|
||||
update_last_error(CApiError {
|
||||
msg: "error converting namespace to UTF-8 string".to_string(),
|
||||
});
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
};
|
||||
let name_str = if let Ok(name) = name.as_str() {
|
||||
name
|
||||
} else {
|
||||
update_last_error(CApiError {
|
||||
msg: "error converting name to UTF-8 string".to_string(),
|
||||
});
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
};
|
||||
if import.is_null() || import_export_value.is_null() {
|
||||
update_last_error(CApiError {
|
||||
msg: "pointers to import and import_export_value must not be null".to_string(),
|
||||
});
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
}
|
||||
let import_out = &mut *import;
|
||||
let import_export_value_out = &mut *import_export_value;
|
||||
if let Some(export) =
|
||||
import_object.maybe_with_namespace(namespace_str, |ns| ns.get_export(name_str))
|
||||
{
|
||||
match export {
|
||||
Export::Function { .. } => {
|
||||
if tag != wasmer_import_export_kind::WASM_FUNCTION {
|
||||
update_last_error(CApiError {
|
||||
msg: format!("Found function, expected {}", tag.to_str()),
|
||||
});
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
}
|
||||
import_out.tag = wasmer_import_export_kind::WASM_FUNCTION;
|
||||
let writer = import_export_value_out.func as *mut Export;
|
||||
*writer = export.clone();
|
||||
}
|
||||
Export::Memory(memory) => {
|
||||
if tag != wasmer_import_export_kind::WASM_MEMORY {
|
||||
update_last_error(CApiError {
|
||||
msg: format!("Found memory, expected {}", tag.to_str()),
|
||||
});
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
}
|
||||
import_out.tag = wasmer_import_export_kind::WASM_MEMORY;
|
||||
let writer = import_export_value_out.func as *mut Memory;
|
||||
*writer = memory.clone();
|
||||
}
|
||||
Export::Table(table) => {
|
||||
if tag != wasmer_import_export_kind::WASM_TABLE {
|
||||
update_last_error(CApiError {
|
||||
msg: format!("Found table, expected {}", tag.to_str()),
|
||||
});
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
}
|
||||
import_out.tag = wasmer_import_export_kind::WASM_TABLE;
|
||||
let writer = import_export_value_out.func as *mut Table;
|
||||
*writer = table.clone();
|
||||
}
|
||||
Export::Global(global) => {
|
||||
if tag != wasmer_import_export_kind::WASM_GLOBAL {
|
||||
update_last_error(CApiError {
|
||||
msg: format!("Found global, expected {}", tag.to_str()),
|
||||
});
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
}
|
||||
import_out.tag = wasmer_import_export_kind::WASM_GLOBAL;
|
||||
let writer = import_export_value_out.func as *mut Global;
|
||||
*writer = global.clone();
|
||||
}
|
||||
}
|
||||
|
||||
import_out.value = *import_export_value;
|
||||
import_out.module_name = namespace;
|
||||
import_out.import_name = name;
|
||||
|
||||
wasmer_result_t::WASMER_OK
|
||||
} else {
|
||||
update_last_error(CApiError {
|
||||
msg: format!("Export {} {} not found", namespace_str, name_str),
|
||||
});
|
||||
wasmer_result_t::WASMER_ERROR
|
||||
}
|
||||
}
|
||||
|
||||
/// private wrapper data type used for casting
|
||||
#[repr(C)]
|
||||
struct WasmerImportObjectIterator(
|
||||
std::iter::Peekable<Box<dyn Iterator<Item = <ImportObjectIterator as Iterator>::Item>>>,
|
||||
);
|
||||
|
||||
/// Create an iterator over the functions in the import object.
|
||||
/// Get the next import with `wasmer_import_object_iter_next`
|
||||
/// Free the iterator with `wasmer_import_object_iter_destroy`
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_import_object_iterate_functions(
|
||||
import_object: *const wasmer_import_object_t,
|
||||
) -> *mut wasmer_import_object_iter_t {
|
||||
if import_object.is_null() {
|
||||
update_last_error(CApiError {
|
||||
msg: "import_object must not be null".to_owned(),
|
||||
});
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
let import_object: &ImportObject = &*(import_object as *const ImportObject);
|
||||
let iter_inner = Box::new(import_object.clone_ref().into_iter().filter(|(_, _, e)| {
|
||||
if let Export::Function { .. } = e {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})) as Box<dyn Iterator<Item = <ImportObjectIterator as Iterator>::Item>>;
|
||||
let iterator = Box::new(WasmerImportObjectIterator(iter_inner.peekable()));
|
||||
|
||||
Box::into_raw(iterator) as *mut wasmer_import_object_iter_t
|
||||
}
|
||||
|
||||
/// Writes the next value to `import`. `WASMER_ERROR` is returned if there
|
||||
/// was an error or there's nothing left to return.
|
||||
///
|
||||
/// To free the memory allocated here, pass the import to `wasmer_import_object_imports_destroy`.
|
||||
/// To check if the iterator is done, use `wasmer_import_object_iter_at_end`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_import_object_iter_next(
|
||||
import_object_iter: *mut wasmer_import_object_iter_t,
|
||||
import: *mut wasmer_import_t,
|
||||
) -> wasmer_result_t {
|
||||
if import_object_iter.is_null() || import.is_null() {
|
||||
update_last_error(CApiError {
|
||||
msg: "import_object_iter and import must not be null".to_owned(),
|
||||
});
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
}
|
||||
|
||||
let iter = &mut *(import_object_iter as *mut WasmerImportObjectIterator);
|
||||
let out = &mut *import;
|
||||
// TODO: the copying here can be optimized away, we just need to use a different type of
|
||||
// iterator internally
|
||||
if let Some((namespace, name, export)) = iter.0.next() {
|
||||
let ns = {
|
||||
let mut n = namespace.clone();
|
||||
n.shrink_to_fit();
|
||||
n.into_bytes()
|
||||
};
|
||||
let ns_bytes = wasmer_byte_array {
|
||||
bytes: ns.as_ptr(),
|
||||
bytes_len: ns.len() as u32,
|
||||
};
|
||||
|
||||
let name = {
|
||||
let mut n = name.clone();
|
||||
n.shrink_to_fit();
|
||||
n.into_bytes()
|
||||
};
|
||||
let name_bytes = wasmer_byte_array {
|
||||
bytes: name.as_ptr(),
|
||||
bytes_len: name.len() as u32,
|
||||
};
|
||||
|
||||
out.module_name = ns_bytes;
|
||||
out.import_name = name_bytes;
|
||||
|
||||
std::mem::forget(ns);
|
||||
std::mem::forget(name);
|
||||
|
||||
match export {
|
||||
Export::Function { .. } => {
|
||||
let func = Box::new(export.clone());
|
||||
|
||||
out.tag = wasmer_import_export_kind::WASM_FUNCTION;
|
||||
out.value = wasmer_import_export_value {
|
||||
func: Box::into_raw(func) as *mut _ as *const _,
|
||||
};
|
||||
}
|
||||
Export::Global(global) => {
|
||||
let glbl = Box::new(global.clone());
|
||||
|
||||
out.tag = wasmer_import_export_kind::WASM_GLOBAL;
|
||||
out.value = wasmer_import_export_value {
|
||||
global: Box::into_raw(glbl) as *mut _ as *const _,
|
||||
};
|
||||
}
|
||||
Export::Memory(memory) => {
|
||||
let mem = Box::new(memory.clone());
|
||||
|
||||
out.tag = wasmer_import_export_kind::WASM_MEMORY;
|
||||
out.value = wasmer_import_export_value {
|
||||
memory: Box::into_raw(mem) as *mut _ as *const _,
|
||||
};
|
||||
}
|
||||
Export::Table(table) => {
|
||||
let tbl = Box::new(table.clone());
|
||||
|
||||
out.tag = wasmer_import_export_kind::WASM_TABLE;
|
||||
out.value = wasmer_import_export_value {
|
||||
memory: Box::into_raw(tbl) as *mut _ as *const _,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
wasmer_result_t::WASMER_OK
|
||||
} else {
|
||||
wasmer_result_t::WASMER_ERROR
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if further calls to `wasmer_import_object_iter_next` will
|
||||
/// not return any new data
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_import_object_iter_at_end(
|
||||
import_object_iter: *mut wasmer_import_object_iter_t,
|
||||
) -> bool {
|
||||
if import_object_iter.is_null() {
|
||||
update_last_error(CApiError {
|
||||
msg: "import_object_iter must not be null".to_owned(),
|
||||
});
|
||||
return true;
|
||||
}
|
||||
let iter = &mut *(import_object_iter as *mut WasmerImportObjectIterator);
|
||||
|
||||
iter.0.peek().is_none()
|
||||
}
|
||||
|
||||
/// Frees the memory allocated by `wasmer_import_object_iterate_functions`
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_import_object_iter_destroy(
|
||||
import_object_iter: *mut wasmer_import_object_iter_t,
|
||||
) {
|
||||
if !import_object_iter.is_null() {
|
||||
let _ = Box::from_raw(import_object_iter as *mut WasmerImportObjectIterator);
|
||||
}
|
||||
}
|
||||
|
||||
/// Frees the memory allocated in `wasmer_import_object_iter_next`
|
||||
///
|
||||
/// This function does not free the memory in `wasmer_import_object_t`;
|
||||
/// it only frees memory allocated while querying a `wasmer_import_object_t`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_import_object_imports_destroy(
|
||||
imports: *mut wasmer_import_t,
|
||||
imports_len: u32,
|
||||
) {
|
||||
if imports.is_null() {
|
||||
return;
|
||||
}
|
||||
let imports: &[wasmer_import_t] = &*slice::from_raw_parts_mut(imports, imports_len as usize);
|
||||
for import in imports {
|
||||
let _namespace: Vec<u8> = Vec::from_raw_parts(
|
||||
import.module_name.bytes as *mut u8,
|
||||
import.module_name.bytes_len as usize,
|
||||
import.module_name.bytes_len as usize,
|
||||
);
|
||||
let _name: Vec<u8> = Vec::from_raw_parts(
|
||||
import.import_name.bytes as *mut u8,
|
||||
import.import_name.bytes_len as usize,
|
||||
import.import_name.bytes_len as usize,
|
||||
);
|
||||
match import.tag {
|
||||
wasmer_import_export_kind::WASM_FUNCTION => {
|
||||
let _: Box<Export> = Box::from_raw(import.value.func as *mut _);
|
||||
}
|
||||
wasmer_import_export_kind::WASM_GLOBAL => {
|
||||
let _: Box<Global> = Box::from_raw(import.value.global as *mut _);
|
||||
}
|
||||
wasmer_import_export_kind::WASM_MEMORY => {
|
||||
let _: Box<Memory> = Box::from_raw(import.value.memory as *mut _);
|
||||
}
|
||||
wasmer_import_export_kind::WASM_TABLE => {
|
||||
let _: Box<Table> = Box::from_raw(import.value.table as *mut _);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extends an existing import object with new imports
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_import_object_extend(
|
||||
import_object: *mut wasmer_import_object_t,
|
||||
imports: *mut wasmer_import_t,
|
||||
imports: *const wasmer_import_t,
|
||||
imports_len: c_uint,
|
||||
) -> wasmer_result_t {
|
||||
let import_object: &mut ImportObject = &mut *(import_object as *mut ImportObject);
|
101
lib/runtime-c-api/src/import/wasi.rs
Normal file
101
lib/runtime-c-api/src/import/wasi.rs
Normal file
@ -0,0 +1,101 @@
|
||||
use super::*;
|
||||
use crate::get_slice_checked;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Opens a directory that's visible to the WASI module as `alias` but
|
||||
/// is backed by the host file at `host_file_path`
|
||||
#[repr(C)]
|
||||
pub struct wasmer_wasi_map_dir_entry_t {
|
||||
/// What the WASI module will see in its virtual root
|
||||
pub alias: wasmer_byte_array,
|
||||
/// The backing file that the WASI module will interact with via the alias
|
||||
pub host_file_path: wasmer_byte_array,
|
||||
}
|
||||
|
||||
impl wasmer_wasi_map_dir_entry_t {
|
||||
/// Converts the data into owned, Rust types
|
||||
pub unsafe fn as_tuple(&self) -> Result<(String, PathBuf), std::str::Utf8Error> {
|
||||
let alias = self.alias.as_str()?.to_owned();
|
||||
let host_path = std::path::PathBuf::from(self.host_file_path.as_str()?);
|
||||
|
||||
Ok((alias, host_path))
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a WASI import object.
|
||||
///
|
||||
/// This function treats null pointers as empty collections.
|
||||
/// For example, passing null for a string in `args`, will lead to a zero
|
||||
/// length argument in that position.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_wasi_generate_import_object(
|
||||
args: *const wasmer_byte_array,
|
||||
args_len: c_uint,
|
||||
envs: *const wasmer_byte_array,
|
||||
envs_len: c_uint,
|
||||
preopened_files: *const wasmer_byte_array,
|
||||
preopened_files_len: c_uint,
|
||||
mapped_dirs: *const wasmer_wasi_map_dir_entry_t,
|
||||
mapped_dirs_len: c_uint,
|
||||
) -> *mut wasmer_import_object_t {
|
||||
let arg_list = get_slice_checked(args, args_len as usize);
|
||||
let env_list = get_slice_checked(envs, envs_len as usize);
|
||||
let preopened_file_list = get_slice_checked(preopened_files, preopened_files_len as usize);
|
||||
let mapped_dir_list = get_slice_checked(mapped_dirs, mapped_dirs_len as usize);
|
||||
|
||||
wasmer_wasi_generate_import_object_inner(
|
||||
arg_list,
|
||||
env_list,
|
||||
preopened_file_list,
|
||||
mapped_dir_list,
|
||||
)
|
||||
.unwrap_or(std::ptr::null_mut())
|
||||
}
|
||||
|
||||
/// Inner function that wraps error handling
|
||||
fn wasmer_wasi_generate_import_object_inner(
|
||||
arg_list: &[wasmer_byte_array],
|
||||
env_list: &[wasmer_byte_array],
|
||||
preopened_file_list: &[wasmer_byte_array],
|
||||
mapped_dir_list: &[wasmer_wasi_map_dir_entry_t],
|
||||
) -> Result<*mut wasmer_import_object_t, std::str::Utf8Error> {
|
||||
let arg_vec = arg_list.iter().map(|arg| unsafe { arg.as_vec() }).collect();
|
||||
let env_vec = env_list
|
||||
.iter()
|
||||
.map(|env_var| unsafe { env_var.as_vec() })
|
||||
.collect();
|
||||
let po_file_vec = preopened_file_list
|
||||
.iter()
|
||||
.map(|po_file| Ok(unsafe { PathBuf::from(po_file.as_str()?) }.to_owned()))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let mapped_dir_vec = mapped_dir_list
|
||||
.iter()
|
||||
.map(|entry| unsafe { entry.as_tuple() })
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let import_object = Box::new(wasmer_wasi::generate_import_object(
|
||||
arg_vec,
|
||||
env_vec,
|
||||
po_file_vec,
|
||||
mapped_dir_vec,
|
||||
));
|
||||
Ok(Box::into_raw(import_object) as *mut wasmer_import_object_t)
|
||||
}
|
||||
|
||||
/// Convenience function that creates a WASI import object with no arguments,
|
||||
/// environment variables, preopened files, or mapped directories.
|
||||
///
|
||||
/// This function is the same as calling [`wasmer_wasi_generate_import_object`] with all
|
||||
/// empty values.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_wasi_generate_default_import_object() -> *mut wasmer_import_object_t
|
||||
{
|
||||
let import_object = Box::new(wasmer_wasi::generate_import_object(
|
||||
vec![],
|
||||
vec![],
|
||||
vec![],
|
||||
vec![],
|
||||
));
|
||||
|
||||
Box::into_raw(import_object) as *mut wasmer_import_object_t
|
||||
}
|
@ -75,6 +75,7 @@ pub unsafe extern "C" fn wasmer_instantiate(
|
||||
|
||||
let namespace = namespaces.entry(module_name).or_insert_with(Namespace::new);
|
||||
|
||||
// TODO check that tag is actually in bounds here
|
||||
let export = match import.tag {
|
||||
wasmer_import_export_kind::WASM_MEMORY => {
|
||||
let mem = import.value.memory as *mut Memory;
|
||||
|
@ -129,3 +129,34 @@ pub struct wasmer_byte_array {
|
||||
pub bytes: *const u8,
|
||||
pub bytes_len: u32,
|
||||
}
|
||||
|
||||
impl wasmer_byte_array {
|
||||
/// Get the data as a slice
|
||||
pub unsafe fn as_slice<'a>(&self) -> &'a [u8] {
|
||||
get_slice_checked(self.bytes, self.bytes_len as usize)
|
||||
}
|
||||
|
||||
/// Copy the data into an owned Vec
|
||||
pub unsafe fn as_vec(&self) -> Vec<u8> {
|
||||
let mut out = Vec::with_capacity(self.bytes_len as usize);
|
||||
out.extend_from_slice(self.as_slice());
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
/// Read the data as a &str, returns an error if the string is not valid UTF8
|
||||
pub unsafe fn as_str<'a>(&self) -> Result<&'a str, std::str::Utf8Error> {
|
||||
std::str::from_utf8(self.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets a slice from a pointer and a length, returning an empty slice if the
|
||||
/// pointer is null
|
||||
#[inline]
|
||||
pub(crate) unsafe fn get_slice_checked<'a, T>(ptr: *const T, len: usize) -> &'a [T] {
|
||||
if ptr.is_null() {
|
||||
&[]
|
||||
} else {
|
||||
std::slice::from_raw_parts(ptr, len)
|
||||
}
|
||||
}
|
||||
|
2
lib/runtime-c-api/tests/.gitignore
vendored
2
lib/runtime-c-api/tests/.gitignore
vendored
@ -25,3 +25,5 @@ test-tables
|
||||
test-validate
|
||||
test-context
|
||||
test-module-import-instantiate
|
||||
test-wasi-import-object
|
||||
|
||||
|
@ -6,6 +6,8 @@ add_executable(test-exports test-exports.c)
|
||||
add_executable(test-globals test-globals.c)
|
||||
add_executable(test-import-function test-import-function.c)
|
||||
add_executable(test-imports test-imports.c)
|
||||
add_executable(test-import-object test-import-object.c)
|
||||
add_executable(test-wasi-import-object test-wasi-import-object.c)
|
||||
add_executable(test-instantiate test-instantiate.c)
|
||||
add_executable(test-memory test-memory.c)
|
||||
add_executable(test-module test-module.c)
|
||||
@ -58,6 +60,14 @@ target_link_libraries(test-imports general ${WASMER_LIB})
|
||||
target_compile_options(test-imports PRIVATE ${COMPILER_OPTIONS})
|
||||
add_test(test-imports test-imports)
|
||||
|
||||
target_link_libraries(test-import-object general ${WASMER_LIB})
|
||||
target_compile_options(test-import-object PRIVATE ${COMPILER_OPTIONS})
|
||||
add_test(test-import-object test-import-object)
|
||||
|
||||
target_link_libraries(test-wasi-import-object general ${WASMER_LIB})
|
||||
target_compile_options(test-wasi-import-object PRIVATE ${COMPILER_OPTIONS})
|
||||
add_test(test-wasi-import-object test-wasi-import-object)
|
||||
|
||||
target_link_libraries(test-instantiate general ${WASMER_LIB})
|
||||
target_compile_options(test-instantiate PRIVATE ${COMPILER_OPTIONS})
|
||||
add_test(test-instantiate test-instantiate)
|
||||
|
3
lib/runtime-c-api/tests/assets/README.md
Normal file
3
lib/runtime-c-api/tests/assets/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
These are used in tests in the parent directory.
|
||||
|
||||
To keep the generated wasm small, use `wasm-opt` and `wasm-strip` from wabt-tools (can be installed via wapm). Addtionally, consider passing the `-C opt-level=z` flag to `rustc` to optimize for size.
|
31
lib/runtime-c-api/tests/assets/extended_wasi.rs
Normal file
31
lib/runtime-c-api/tests/assets/extended_wasi.rs
Normal file
@ -0,0 +1,31 @@
|
||||
extern "C" {
|
||||
fn host_print(ptr: u32, len: u32);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = std::env::args().collect::<Vec<String>>();
|
||||
|
||||
println!("Found {} args on program {}", args.len(), args[0]);
|
||||
|
||||
let env_vars = std::env::vars()
|
||||
.map(|(arg, val)| format!("{}={}", arg, val))
|
||||
.collect::<Vec<String>>();
|
||||
let env_var_list = env_vars.join(", ");
|
||||
|
||||
println!("Found {} env vars: {}", env_vars.len(), env_var_list);
|
||||
|
||||
let dirs_in_root = std::fs::read_dir("/")
|
||||
.unwrap()
|
||||
.map(|e| e.map(|inner| format!("{:?}", inner)))
|
||||
.collect::<Result<Vec<String>, _>>()
|
||||
.unwrap();
|
||||
|
||||
println!(
|
||||
"Found {} pre opened dirs: {}",
|
||||
dirs_in_root.len(),
|
||||
dirs_in_root.join(", ")
|
||||
);
|
||||
|
||||
const HOST_STR: &str = "This string came from a WASI module";
|
||||
unsafe { host_print(HOST_STR.as_ptr() as u32, HOST_STR.len() as u32) };
|
||||
}
|
BIN
lib/runtime-c-api/tests/assets/extended_wasi.wasm
Executable file
BIN
lib/runtime-c-api/tests/assets/extended_wasi.wasm
Executable file
Binary file not shown.
@ -1,3 +1,4 @@
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include "../wasmer.h"
|
||||
#include <assert.h>
|
||||
@ -242,7 +243,7 @@ int main()
|
||||
|
||||
wasmer_result_t call_result = wasmer_export_func_call(exported_function, inputs, inputs_arity, outputs, outputs_arity);
|
||||
|
||||
printf("Result: %ld\n", outputs[0].value.I64);
|
||||
printf("Result: %" PRId64 "\n", outputs[0].value.I64);
|
||||
|
||||
assert(outputs[0].value.I64 == 7);
|
||||
assert(call_result == WASMER_OK);
|
||||
|
BIN
lib/runtime-c-api/tests/test-import-object
Executable file
BIN
lib/runtime-c-api/tests/test-import-object
Executable file
Binary file not shown.
172
lib/runtime-c-api/tests/test-import-object.c
Normal file
172
lib/runtime-c-api/tests/test-import-object.c
Normal file
@ -0,0 +1,172 @@
|
||||
#include <stdio.h>
|
||||
#include "../wasmer.h"
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
bool static print_str_called = false;
|
||||
|
||||
// Host function that will be imported into the Web Assembly Instance
|
||||
void print_str(const wasmer_instance_context_t *ctx, int32_t ptr, int32_t len)
|
||||
{
|
||||
print_str_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);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// Create a new func to hold the parameter and signature
|
||||
// of our `print_str` 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 *)) print_str, 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 = "_print_str";
|
||||
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;
|
||||
|
||||
// Define an empty import object
|
||||
wasmer_import_object_t *import_object = wasmer_import_object_new();
|
||||
// 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);
|
||||
|
||||
// Read the wasm file bytes
|
||||
FILE *file = fopen("assets/hello_wasm.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;
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// Call the exported "hello_wasm" function of our instance
|
||||
wasmer_value_t params[] = {};
|
||||
wasmer_value_t result_one;
|
||||
wasmer_value_t results[] = {result_one};
|
||||
wasmer_result_t call_result = wasmer_instance_call(instance, "_hello_wasm", params, 0, results, 1);
|
||||
printf("Call result: %d\n", call_result);
|
||||
assert(call_result == WASMER_OK);
|
||||
assert(print_str_called);
|
||||
|
||||
// Use *_destroy methods to cleanup as specified in the header documentation
|
||||
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;
|
||||
}
|
BIN
lib/runtime-c-api/tests/test-wasi-import-object
Executable file
BIN
lib/runtime-c-api/tests/test-wasi-import-object
Executable file
Binary file not shown.
250
lib/runtime-c-api/tests/test-wasi-import-object.c
Normal file
250
lib/runtime-c-api/tests/test-wasi-import-object.c
Normal file
@ -0,0 +1,250 @@
|
||||
#include <stdio.h>
|
||||
#include "../wasmer.h"
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
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
|
||||
|
||||
// Set up data for our WASI import object
|
||||
//
|
||||
// Environment variables and program arguments are processed by the WASI
|
||||
// program. They will not have any effects unless the program includes
|
||||
// logic to process them.
|
||||
const char *wasi_prog_name = "wasi_test_program";
|
||||
const char *wasi_first_arg = "--help";
|
||||
wasmer_byte_array args[] = {
|
||||
{ .bytes = (const uint8_t *) wasi_prog_name,
|
||||
.bytes_len = strlen(wasi_prog_name) },
|
||||
{ .bytes = (const uint8_t *) wasi_first_arg,
|
||||
.bytes_len = strlen(wasi_first_arg) }
|
||||
};
|
||||
int wasi_argc = sizeof(args) / sizeof(args[0]);
|
||||
|
||||
// Create arbitrary environment variables for our program;
|
||||
const char *wasi_color_env = "COLOR=TRUE";
|
||||
const char *wasi_app_should_log = "APP_SHOULD_LOG=FALSE";
|
||||
wasmer_byte_array envs[] = {
|
||||
{ .bytes = (const uint8_t *) wasi_color_env,
|
||||
.bytes_len = strlen(wasi_color_env) },
|
||||
{ .bytes = (const uint8_t *) wasi_app_should_log,
|
||||
.bytes_len = strlen(wasi_app_should_log) }
|
||||
};
|
||||
int wasi_env_len = sizeof(args) / sizeof(args[0]);
|
||||
|
||||
// Open the host's current directory under a different name.
|
||||
// WARNING: this gives the WASI module limited access to your host's file system,
|
||||
// use caution when granting these permissions to untrusted Wasm modules.
|
||||
const char *wasi_map_dir_alias = "the_host_current_dir";
|
||||
const char *wasi_map_dir_host_path = ".";
|
||||
wasmer_wasi_map_dir_entry_t mapped_dirs[] = {
|
||||
{ .alias =
|
||||
{ .bytes = (const uint8_t *) wasi_map_dir_alias,
|
||||
.bytes_len = strlen(wasi_map_dir_alias) },
|
||||
.host_file_path =
|
||||
{ .bytes = (const uint8_t *) wasi_map_dir_host_path,
|
||||
.bytes_len = strlen(wasi_map_dir_host_path) } }
|
||||
};
|
||||
int mapped_dir_len = sizeof(mapped_dirs) / sizeof(mapped_dirs[0]);
|
||||
|
||||
// Create the WASI import object
|
||||
wasmer_import_object_t *import_object =
|
||||
wasmer_wasi_generate_import_object(args, wasi_argc,
|
||||
envs, wasi_env_len,
|
||||
NULL, 0,
|
||||
mapped_dirs, mapped_dir_len);
|
||||
|
||||
// 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);
|
||||
|
||||
// Read the wasm file bytes
|
||||
FILE *file = fopen("assets/extended_wasi.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);
|
||||
|
||||
// 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);
|
||||
|
||||
// Call the exported "hello_wasm" function of our instance
|
||||
wasmer_value_t params[] = {};
|
||||
wasmer_value_t result_one;
|
||||
wasmer_value_t results[] = {result_one};
|
||||
// _start runs before main for WASI programs
|
||||
wasmer_result_t call_result = wasmer_instance_call(instance, "_start", params, 0, results, 1);
|
||||
printf("Call result: %d\n", call_result);
|
||||
assert(call_result == WASMER_OK);
|
||||
assert(host_print_called);
|
||||
|
||||
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_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;
|
||||
}
|
||||
|
@ -10,10 +10,10 @@
|
||||
* List of export/import kinds.
|
||||
*/
|
||||
enum wasmer_import_export_kind {
|
||||
WASM_FUNCTION,
|
||||
WASM_GLOBAL,
|
||||
WASM_MEMORY,
|
||||
WASM_TABLE,
|
||||
WASM_FUNCTION = 0,
|
||||
WASM_GLOBAL = 1,
|
||||
WASM_MEMORY = 2,
|
||||
WASM_TABLE = 3,
|
||||
};
|
||||
typedef uint32_t wasmer_import_export_kind;
|
||||
|
||||
@ -138,6 +138,10 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
|
||||
} wasmer_import_object_iter_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
} wasmer_instance_t;
|
||||
|
||||
typedef struct {
|
||||
@ -170,6 +174,21 @@ typedef struct {
|
||||
|
||||
} wasmer_trampoline_buffer_t;
|
||||
|
||||
/**
|
||||
* Opens a directory that's visible to the WASI module as `alias` but
|
||||
* is backed by the host file at `host_file_path`
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* What the WASI module will see in its virtual root
|
||||
*/
|
||||
wasmer_byte_array alias;
|
||||
/**
|
||||
* The backing file that the WASI module will interact with via the alias
|
||||
*/
|
||||
wasmer_byte_array host_file_path;
|
||||
} wasmer_wasi_map_dir_entry_t;
|
||||
|
||||
/**
|
||||
* Creates a new Module from the given wasm bytes.
|
||||
*
|
||||
@ -451,9 +470,60 @@ void wasmer_import_object_destroy(wasmer_import_object_t *import_object);
|
||||
* Extends an existing import object with new imports
|
||||
*/
|
||||
wasmer_result_t wasmer_import_object_extend(wasmer_import_object_t *import_object,
|
||||
wasmer_import_t *imports,
|
||||
const wasmer_import_t *imports,
|
||||
unsigned int imports_len);
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
*
|
||||
* The caller owns all data involved.
|
||||
* `import_export_value` will be written to based on `tag`.
|
||||
*/
|
||||
wasmer_result_t wasmer_import_object_get_import(const wasmer_import_object_t *import_object,
|
||||
wasmer_byte_array namespace_,
|
||||
wasmer_byte_array name,
|
||||
wasmer_import_t *import,
|
||||
wasmer_import_export_value *import_export_value,
|
||||
uint32_t tag);
|
||||
|
||||
/**
|
||||
* Frees the memory allocated in `wasmer_import_object_iter_next`
|
||||
*
|
||||
* This function does not free the memory in `wasmer_import_object_t`;
|
||||
* it only frees memory allocated while querying a `wasmer_import_object_t`.
|
||||
*/
|
||||
void wasmer_import_object_imports_destroy(wasmer_import_t *imports, uint32_t imports_len);
|
||||
|
||||
/**
|
||||
* Returns true if further calls to `wasmer_import_object_iter_next` will
|
||||
* not return any new data
|
||||
*/
|
||||
bool wasmer_import_object_iter_at_end(wasmer_import_object_iter_t *import_object_iter);
|
||||
|
||||
/**
|
||||
* Frees the memory allocated by `wasmer_import_object_iterate_functions`
|
||||
*/
|
||||
void wasmer_import_object_iter_destroy(wasmer_import_object_iter_t *import_object_iter);
|
||||
|
||||
/**
|
||||
* Writes the next value to `import`. `WASMER_ERROR` is returned if there
|
||||
* was an error or there's nothing left to return.
|
||||
*
|
||||
* To free the memory allocated here, pass the import to `wasmer_import_object_imports_destroy`.
|
||||
* To check if the iterator is done, use `wasmer_import_object_iter_at_end`.
|
||||
*/
|
||||
wasmer_result_t wasmer_import_object_iter_next(wasmer_import_object_iter_t *import_object_iter,
|
||||
wasmer_import_t *import);
|
||||
|
||||
/**
|
||||
* Create an iterator over the functions in the import object.
|
||||
* Get the next import with `wasmer_import_object_iter_next`
|
||||
* Free the iterator with `wasmer_import_object_iter_destroy`
|
||||
*/
|
||||
wasmer_import_object_iter_t *wasmer_import_object_iterate_functions(const wasmer_import_object_t *import_object);
|
||||
|
||||
/**
|
||||
* Creates a new empty import object.
|
||||
* See also `wasmer_import_object_append`
|
||||
@ -756,4 +826,29 @@ void *wasmer_trampoline_get_context(void);
|
||||
*/
|
||||
bool wasmer_validate(const uint8_t *wasm_bytes, uint32_t wasm_bytes_len);
|
||||
|
||||
/**
|
||||
* Convenience function that creates a WASI import object with no arguments,
|
||||
* environment variables, preopened files, or mapped directories.
|
||||
*
|
||||
* This function is the same as calling [`wasmer_wasi_generate_import_object`] with all
|
||||
* empty values.
|
||||
*/
|
||||
wasmer_import_object_t *wasmer_wasi_generate_default_import_object(void);
|
||||
|
||||
/**
|
||||
* Creates a WASI import object.
|
||||
*
|
||||
* This function treats null pointers as empty collections.
|
||||
* For example, passing null for a string in `args`, will lead to a zero
|
||||
* length argument in that position.
|
||||
*/
|
||||
wasmer_import_object_t *wasmer_wasi_generate_import_object(const wasmer_byte_array *args,
|
||||
unsigned int args_len,
|
||||
const wasmer_byte_array *envs,
|
||||
unsigned int envs_len,
|
||||
const wasmer_byte_array *preopened_files,
|
||||
unsigned int preopened_files_len,
|
||||
const wasmer_wasi_map_dir_entry_t *mapped_dirs,
|
||||
unsigned int mapped_dirs_len);
|
||||
|
||||
#endif /* WASMER_H */
|
||||
|
@ -8,10 +8,10 @@
|
||||
|
||||
/// List of export/import kinds.
|
||||
enum class wasmer_import_export_kind : uint32_t {
|
||||
WASM_FUNCTION,
|
||||
WASM_GLOBAL,
|
||||
WASM_MEMORY,
|
||||
WASM_TABLE,
|
||||
WASM_FUNCTION = 0,
|
||||
WASM_GLOBAL = 1,
|
||||
WASM_MEMORY = 2,
|
||||
WASM_TABLE = 3,
|
||||
};
|
||||
|
||||
enum class wasmer_result_t {
|
||||
@ -120,6 +120,10 @@ struct wasmer_import_t {
|
||||
wasmer_import_export_value value;
|
||||
};
|
||||
|
||||
struct wasmer_import_object_iter_t {
|
||||
|
||||
};
|
||||
|
||||
struct wasmer_instance_t {
|
||||
|
||||
};
|
||||
@ -154,6 +158,15 @@ struct wasmer_trampoline_buffer_t {
|
||||
|
||||
};
|
||||
|
||||
/// Opens a directory that's visible to the WASI module as `alias` but
|
||||
/// is backed by the host file at `host_file_path`
|
||||
struct wasmer_wasi_map_dir_entry_t {
|
||||
/// What the WASI module will see in its virtual root
|
||||
wasmer_byte_array alias;
|
||||
/// The backing file that the WASI module will interact with via the alias
|
||||
wasmer_byte_array host_file_path;
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
/// Creates a new Module from the given wasm bytes.
|
||||
@ -359,9 +372,48 @@ void wasmer_import_object_destroy(wasmer_import_object_t *import_object);
|
||||
|
||||
/// Extends an existing import object with new imports
|
||||
wasmer_result_t wasmer_import_object_extend(wasmer_import_object_t *import_object,
|
||||
wasmer_import_t *imports,
|
||||
const wasmer_import_t *imports,
|
||||
unsigned int imports_len);
|
||||
|
||||
/// 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`.
|
||||
///
|
||||
/// The caller owns all data involved.
|
||||
/// `import_export_value` will be written to based on `tag`.
|
||||
wasmer_result_t wasmer_import_object_get_import(const wasmer_import_object_t *import_object,
|
||||
wasmer_byte_array namespace_,
|
||||
wasmer_byte_array name,
|
||||
wasmer_import_t *import,
|
||||
wasmer_import_export_value *import_export_value,
|
||||
uint32_t tag);
|
||||
|
||||
/// Frees the memory allocated in `wasmer_import_object_iter_next`
|
||||
///
|
||||
/// This function does not free the memory in `wasmer_import_object_t`;
|
||||
/// it only frees memory allocated while querying a `wasmer_import_object_t`.
|
||||
void wasmer_import_object_imports_destroy(wasmer_import_t *imports, uint32_t imports_len);
|
||||
|
||||
/// Returns true if further calls to `wasmer_import_object_iter_next` will
|
||||
/// not return any new data
|
||||
bool wasmer_import_object_iter_at_end(wasmer_import_object_iter_t *import_object_iter);
|
||||
|
||||
/// Frees the memory allocated by `wasmer_import_object_iterate_functions`
|
||||
void wasmer_import_object_iter_destroy(wasmer_import_object_iter_t *import_object_iter);
|
||||
|
||||
/// Writes the next value to `import`. `WASMER_ERROR` is returned if there
|
||||
/// was an error or there's nothing left to return.
|
||||
///
|
||||
/// To free the memory allocated here, pass the import to `wasmer_import_object_imports_destroy`.
|
||||
/// To check if the iterator is done, use `wasmer_import_object_iter_at_end`.
|
||||
wasmer_result_t wasmer_import_object_iter_next(wasmer_import_object_iter_t *import_object_iter,
|
||||
wasmer_import_t *import);
|
||||
|
||||
/// Create an iterator over the functions in the import object.
|
||||
/// Get the next import with `wasmer_import_object_iter_next`
|
||||
/// Free the iterator with `wasmer_import_object_iter_destroy`
|
||||
wasmer_import_object_iter_t *wasmer_import_object_iterate_functions(const wasmer_import_object_t *import_object);
|
||||
|
||||
/// Creates a new empty import object.
|
||||
/// See also `wasmer_import_object_append`
|
||||
wasmer_import_object_t *wasmer_import_object_new();
|
||||
@ -590,6 +642,27 @@ void *wasmer_trampoline_get_context();
|
||||
/// Returns true for valid wasm bytes and false for invalid bytes
|
||||
bool wasmer_validate(const uint8_t *wasm_bytes, uint32_t wasm_bytes_len);
|
||||
|
||||
/// Convenience function that creates a WASI import object with no arguments,
|
||||
/// environment variables, preopened files, or mapped directories.
|
||||
///
|
||||
/// This function is the same as calling [`wasmer_wasi_generate_import_object`] with all
|
||||
/// empty values.
|
||||
wasmer_import_object_t *wasmer_wasi_generate_default_import_object();
|
||||
|
||||
/// Creates a WASI import object.
|
||||
///
|
||||
/// This function treats null pointers as empty collections.
|
||||
/// For example, passing null for a string in `args`, will lead to a zero
|
||||
/// length argument in that position.
|
||||
wasmer_import_object_t *wasmer_wasi_generate_import_object(const wasmer_byte_array *args,
|
||||
unsigned int args_len,
|
||||
const wasmer_byte_array *envs,
|
||||
unsigned int envs_len,
|
||||
const wasmer_byte_array *preopened_files,
|
||||
unsigned int preopened_files_len,
|
||||
const wasmer_wasi_map_dir_entry_t *mapped_dirs,
|
||||
unsigned int mapped_dirs_len);
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#endif // WASMER_H
|
||||
|
Loading…
x
Reference in New Issue
Block a user