Skip to content

Commit

Permalink
Merge pull request #217 from wasmerio/feature/wasi-for-the-wasm-c-api
Browse files Browse the repository at this point in the history
Implement WASI for the Wasm C API
  • Loading branch information
MarkMcCaskey committed Aug 13, 2020
2 parents f6766c4 + 8a9ac6c commit c6edf96
Show file tree
Hide file tree
Showing 15 changed files with 870 additions and 35 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion lib/c-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ cfg-if = "0.1"
lazy_static = "1"
libc = { version = "^0.2.69", default-features = false }
libffi = { version = "0.9" }
serde = { version = "1", optional = true, features = ["derive"] }
thiserror = "1"
typetag = { version = "0.1", optional = true }
paste = "0.1"
# for generating code in the same way thot the wasm-c-api does
# Commented out for now until we can find a solution to the exported function problem
Expand All @@ -41,7 +43,7 @@ default = [
"cranelift",
"wasi",
]
wasi = ["wasmer-wasi"]
wasi = ["wasmer-wasi", "typetag", "serde"]
engine = []
jit = [
"wasmer-engine-jit",
Expand Down
4 changes: 2 additions & 2 deletions lib/c-api/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ pub extern "C" fn wasmer_last_error_length() -> c_int {
/// error occurs. Potential errors are:
///
/// * The buffer is a null pointer,
/// * The buffer is too smal to hold the error message.
/// * The buffer is too small to hold the error message.
///
/// Note: The error message always has a trailing null character.
/// Note: The error message always has a trailing NUL character.
///
/// Example:
///
Expand Down
148 changes: 122 additions & 26 deletions lib/c-api/src/wasm_c_api.rs → lib/c-api/src/wasm_c_api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,23 @@ use std::ptr::{self, NonNull};
use std::slice;
use std::sync::Arc;

pub(crate) mod utils;
#[cfg(feature = "wasi")]
pub mod wasi;

// required due to really weird Rust resolution rules
// https://github.com/rust-lang/rust/issues/57966
use crate::c_try;

use crate::ordered_resolver::OrderedResolver;
use wasmer::{
Engine, ExportType, Extern, ExternType, Function, FunctionType, Global, GlobalType, Instance,
Memory, MemoryType, Module, Mutability, Pages, RuntimeError, Store, Table, TableType, Val,
ValType,
Engine, ExportType, Extern, ExternType, Function, FunctionType, Global, GlobalType, ImportType,
Instance, Memory, MemoryType, Module, Mutability, Pages, RuntimeError, Store, Table, TableType,
Val, ValType,
};
#[cfg(feature = "jit")]
use wasmer_engine_jit::JIT;

use crate::error::update_last_error;

macro_rules! c_try {
($expr:expr) => {{
let res: Result<_, _> = $expr;
match res {
Ok(val) => val,
Err(err) => {
update_last_error(err);
return None;
}
}
}};
($expr:expr, $e:expr) => {{
let opt: Option<_> = $expr;
c_try!(opt.ok_or_else(|| $e))
}};
}

/// this can be a wasmer-specific type with wasmer-specific functions for manipulating it
#[repr(C)]
pub struct wasm_config_t {}
Expand Down Expand Up @@ -200,7 +189,7 @@ pub unsafe extern "C" fn wasm_instance_exports(

#[repr(C)]
pub struct wasm_module_t {
inner: Arc<Module>,
pub(crate) inner: Arc<Module>,
}

#[no_mangle]
Expand Down Expand Up @@ -241,6 +230,25 @@ pub unsafe extern "C" fn wasm_module_exports(
mem::forget(exports);
}

#[no_mangle]
pub unsafe extern "C" fn wasm_module_imports(
module: &wasm_module_t,
out: &mut wasm_importtype_vec_t,
) {
let mut imports = module
.inner
.imports()
.map(Into::into)
.map(Box::new)
.map(Box::into_raw)
.collect::<Vec<*mut wasm_importtype_t>>();

debug_assert_eq!(imports.len(), imports.capacity());
out.size = imports.len();
out.data = imports.as_mut_ptr();
mem::forget(imports);
}

#[no_mangle]
pub unsafe extern "C" fn wasm_module_deserialize(
store_ptr: Option<NonNull<wasm_store_t>>,
Expand Down Expand Up @@ -864,7 +872,7 @@ pub unsafe extern "C" fn wasm_global_same(
#[repr(C)]
pub struct wasm_memory_t {
// maybe needs to hold onto instance
inner: Memory,
pub(crate) inner: Memory,
}

#[no_mangle]
Expand Down Expand Up @@ -1184,8 +1192,8 @@ pub unsafe extern "C" fn wasm_trap_trace(trap: *const wasm_trap_t, out_ptr: *mut
#[repr(C)]
pub struct wasm_extern_t {
// this is how we ensure the instance stays alive
instance: Option<Arc<Instance>>,
inner: Extern,
pub(crate) instance: Option<Arc<Instance>>,
pub(crate) inner: Extern,
}
wasm_declare_boxed_vec!(extern);

Expand Down Expand Up @@ -1819,3 +1827,91 @@ impl From<&ExportType> for wasm_exporttype_t {
wasm_exporttype_t { name, extern_type }
}
}

// TODO: improve ownership in `importtype_t` (can we safely use `Box<wasm_name_t>` here?)
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct wasm_importtype_t {
module: NonNull<wasm_name_t>,
name: NonNull<wasm_name_t>,
extern_type: NonNull<wasm_externtype_t>,
}

wasm_declare_boxed_vec!(importtype);

#[no_mangle]
pub extern "C" fn wasm_importtype_new(
module: NonNull<wasm_name_t>,
name: NonNull<wasm_name_t>,
extern_type: NonNull<wasm_externtype_t>,
) -> Box<wasm_importtype_t> {
Box::new(wasm_importtype_t {
name,
module,
extern_type,
})
}

#[no_mangle]
pub extern "C" fn wasm_importtype_module(et: &'static wasm_importtype_t) -> &'static wasm_name_t {
unsafe { et.module.as_ref() }
}

#[no_mangle]
pub extern "C" fn wasm_importtype_name(et: &'static wasm_importtype_t) -> &'static wasm_name_t {
unsafe { et.name.as_ref() }
}

#[no_mangle]
pub extern "C" fn wasm_importtype_type(
et: &'static wasm_importtype_t,
) -> &'static wasm_externtype_t {
unsafe { et.extern_type.as_ref() }
}

impl From<ImportType> for wasm_importtype_t {
fn from(other: ImportType) -> Self {
(&other).into()
}
}

impl From<&ImportType> for wasm_importtype_t {
fn from(other: &ImportType) -> Self {
// TODO: double check that freeing String as `Vec<u8>` is valid
let name = {
let mut heap_str: Box<str> = other.name().to_string().into_boxed_str();
let char_ptr = heap_str.as_mut_ptr();
let str_len = heap_str.bytes().len();
let name_inner = wasm_name_t {
size: str_len,
data: char_ptr,
};
Box::leak(heap_str);
unsafe { NonNull::new_unchecked(Box::into_raw(Box::new(name_inner))) }
};

// TODO: double check that freeing String as `Vec<u8>` is valid
let module = {
let mut heap_str: Box<str> = other.module().to_string().into_boxed_str();
let char_ptr = heap_str.as_mut_ptr();
let str_len = heap_str.bytes().len();
let name_inner = wasm_name_t {
size: str_len,
data: char_ptr,
};
Box::leak(heap_str);
unsafe { NonNull::new_unchecked(Box::into_raw(Box::new(name_inner))) }
};

let extern_type = {
let extern_type: wasm_externtype_t = other.ty().into();
unsafe { NonNull::new_unchecked(Box::into_raw(Box::new(extern_type))) }
};

wasm_importtype_t {
name,
module,
extern_type,
}
}
}
17 changes: 17 additions & 0 deletions lib/c-api/src/wasm_c_api/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#[macro_export]
macro_rules! c_try {
($expr:expr) => {{
let res: Result<_, _> = $expr;
match res {
Ok(val) => val,
Err(err) => {
crate::error::update_last_error(err);
return None;
}
}
}};
($expr:expr, $e:expr) => {{
let opt: Option<_> = $expr;
c_try!(opt.ok_or_else(|| $e))
}};
}
95 changes: 95 additions & 0 deletions lib/c-api/src/wasm_c_api/wasi/capture_files.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//! Default implementations for capturing the stdout/stderr output of a WASI program.

use serde::{Deserialize, Serialize};
use std::collections::VecDeque;
use std::io::{self, Read, Seek, Write};
use wasmer_wasi::{WasiFile, WasiFsError};

/// For capturing stdout/stderr. Stores all output in a string.
#[derive(Debug, Serialize, Deserialize)]
pub struct OutputCapturer {
pub(crate) buffer: VecDeque<u8>,
}

impl OutputCapturer {
pub fn new() -> Self {
Self {
buffer: VecDeque::new(),
}
}
}

#[typetag::serde]
impl WasiFile for OutputCapturer {
fn last_accessed(&self) -> u64 {
0
}
fn last_modified(&self) -> u64 {
0
}
fn created_time(&self) -> u64 {
0
}
fn size(&self) -> u64 {
0
}
fn set_len(&mut self, _len: u64) -> Result<(), WasiFsError> {
Ok(())
}
fn unlink(&mut self) -> Result<(), WasiFsError> {
Ok(())
}
fn bytes_available(&self) -> Result<usize, WasiFsError> {
// return an arbitrary amount
Ok(1024)
}
}

// fail when reading or Seeking
impl Read for OutputCapturer {
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
Err(io::Error::new(
io::ErrorKind::Other,
"can not read from capturing stdout",
))
}
fn read_to_end(&mut self, _buf: &mut Vec<u8>) -> std::io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from capturing stdout",
))
}
fn read_to_string(&mut self, _buf: &mut String) -> io::Result<usize> {
Err(io::Error::new(
io::ErrorKind::Other,
"can not read from capturing stdout",
))
}
fn read_exact(&mut self, _buf: &mut [u8]) -> io::Result<()> {
Err(io::Error::new(
io::ErrorKind::Other,
"can not read from capturing stdout",
))
}
}
impl Seek for OutputCapturer {
fn seek(&mut self, _pos: io::SeekFrom) -> io::Result<u64> {
Err(io::Error::new(
io::ErrorKind::Other,
"can not seek capturing stdout",
))
}
}
impl Write for OutputCapturer {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.buffer.extend(buf);
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
self.buffer.extend(buf);
Ok(())
}
}
Loading

0 comments on commit c6edf96

Please sign in to comment.