Merge pull request #1996 from fitzgen/ref-types-in-c-api

Support reference types in the C API
This commit is contained in:
Nick Fitzgerald
2020-07-13 10:58:42 -07:00
committed by GitHub
17 changed files with 592 additions and 291 deletions

View File

@@ -1147,19 +1147,22 @@
/**
* \struct wasm_ref_t
* \brief Unimplemented and used in Wasmtime right now.
* \brief A reference type: either a funcref or an externref.
*
* \typedef wasm_ref_t
* \brief Convenience alias for #wasm_ref_t
*
* \fn void wasm_ref_delete(own wasm_ref_t *v);
* \brief Deletes a reference.
* \brief Delete a reference.
*
* \fn own wasm_ref_t *wasm_ref_copy(const wasm_ref_t *)
* \brief Unimplemented in Wasmtime, aborts the process if called.
* \brief Copy a reference.
*
* \fn bool wasm_ref_same(const wasm_ref_t *, const wasm_ref_t *)
* \brief Unimplemented in Wasmtime, aborts the process if called.
* \brief Are the given references pointing to the same externref?
*
* > Note: Wasmtime does not support checking funcrefs for equality, and this
* > function will always return false for funcrefs.
*
* \fn void* wasm_ref_get_host_info(const wasm_ref_t *);
* \brief Unimplemented in Wasmtime, always returns `NULL`.
@@ -1614,6 +1617,9 @@
* If a trap happens during execution or some other error then a non-`NULL` trap
* is returned. In this situation the `results` are is unmodified.
*
* Does not take ownership of `wasm_val_t` arguments. Gives ownership of
* `wasm_val_t` results.
*
* > Note: to avoid the UB associated with passing the wrong number of results
* > or parameters by accident, or to distinguish between traps and other
* > errors, it's recommended to use #wasmtime_func_call.
@@ -1758,10 +1764,9 @@
* Returns an error if the #wasm_ref_t does not match the element type of the
* table provided or if it comes from a different store than the one provided.
*
* Does not take ownship of the `init` value.
*
* > Note: for funcref tables you can use #wasmtime_funcref_table_new as well.
* >
* > Additionally the #wasm_ref_t does not have much support in Wasmtime, so you
* > may not be able to create an appropriate initial value.
*
* \fn wasm_tabletype_t *wasm_table_type(const wasm_table_t *);
* \brief Returns the type of this table.
@@ -1774,12 +1779,10 @@
* Attempts to get a value at an index in this table. This function returns
* `NULL` if the index is out of bounds.
*
* Gives ownership of the resulting `wasm_ref_t*`.
*
* > Note: for funcref tables you can use #wasmtime_funcref_table_get to learn
* > about out-of-bounds errors.
* >
* > Additionally the #wasm_ref_t does not have much
* > support in Wasmtime, so you may not be able to do much with the returned
* > value.
*
* \fn void wasm_table_set(wasm_table_t *, wasm_table_size_t index, wasm_ref_t *);
* \brief Sets an element in this table.
@@ -1791,11 +1794,10 @@
* * The #wasm_ref_t comes from a different store than the table provided.
* * The #wasm_ref_t does not have an appropriate type to store in this table.
*
* Does not take ownership of the given `wasm_ref_t*`.
*
* > Note: for funcref tables you can use #wasmtime_funcref_table_set to learn
* > about errors.
* >
* > Additionally the #wasm_ref_t does not have much support in Wasmtime, so you
* > may not be able to create an appropriate initial value.
*
* \fn wasm_table_size_t wasm_table_size(const wasm_table_t *);
* \brief Gets the current size, in elements, of this table.
@@ -1813,10 +1815,9 @@
* * The #wasm_ref_t comes from a different store than the table provided.
* * The #wasm_ref_t does not have an appropriate type to store in this table.
*
* Does not take ownership of the givein `init` value.
*
* > Note: for funcref tables you can use #wasmtime_funcref_table_grow as well.
* >
* > Additionally the #wasm_ref_t does not have much support in Wasmtime, so you
* > may not be able to create an appropriate initial value.
*/
/**

View File

@@ -651,6 +651,9 @@ WASM_API_EXTERN const wasm_name_t *wasmtime_frame_module_name(const wasm_frame_t
*
* The `trap` pointer cannot be `NULL`. The `args` and `results` pointers may be
* `NULL` if the corresponding length is zero.
*
* Does not take ownership of `wasm_val_t` arguments. Gives ownership of
* `wasm_val_t` results.
*/
WASM_API_EXTERN own wasmtime_error_t *wasmtime_func_call(
wasm_func_t *func,
@@ -833,6 +836,63 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_funcref_table_grow(
wasm_table_size_t *prev_size
);
/**
* \brief Create a new `externref` value.
*
* Creates a new `externref` value wrapping the provided data, and writes it to
* `valp`.
*
* This function does not take an associated finalizer to clean up the data when
* the reference is reclaimed. If you need a finalizer to clean up the data,
* then use #wasmtime_externref_new_with_finalizer.
*/
WASM_API_EXTERN void wasmtime_externref_new(void *data, wasm_val_t *valp);
/**
* \brief A finalizer for an `externref`'s wrapped data.
*
* A finalizer callback to clean up an `externref`'s wrapped data after the
* `externref` has been reclaimed. This is an opportunity to run destructors,
* free dynamically allocated memory, close file handles, etc.
*/
typedef void (*wasmtime_externref_finalizer_t)(void*);
/**
* \brief Create a new `externref` value with a finalizer.
*
* Creates a new `externref` value wrapping the provided data, and writes it to
* `valp`.
*
* When the reference is reclaimed, the wrapped data is cleaned up with the
* provided finalizer. If you do not need to clean up the wrapped data, then use
* #wasmtime_externref_new.
*/
WASM_API_EXTERN void wasmtime_externref_new_with_finalizer(
void *data,
wasmtime_externref_finalizer_t finalizer,
wasm_val_t *valp
);
/**
* \brief Get an `externref`'s wrapped data
*
* If the given value is a reference to a non-null `externref`, writes the
* wrapped data that was passed into #wasmtime_externref_new or
* #wasmtime_externref_new_with_finalizer when creating the given `externref` to
* `datap`, and returns `true`.
*
* If the value is a reference to a null `externref`, writes `NULL` to `datap`
* and returns `true`.
*
* If the given value is not an `externref`, returns `false` and leaves `datap`
* unmodified.
*
* Does not take ownership of `val`.
*
* Both `val` and `datap` must not be `NULL`.
*/
WASM_API_EXTERN bool wasmtime_externref_data(wasm_val_t* val, void** datap);
#undef own
#ifdef __cplusplus

View File

@@ -2,6 +2,7 @@ use crate::{wasm_extern_t, wasm_functype_t, wasm_store_t, wasm_val_t};
use crate::{wasm_name_t, wasm_trap_t, wasmtime_error_t};
use anyhow::anyhow;
use std::ffi::c_void;
use std::mem::MaybeUninit;
use std::panic::{self, AssertUnwindSafe};
use std::ptr;
use std::str;
@@ -89,6 +90,7 @@ fn create_function(
let func = Func::new(store, ty, move |caller, params, results| {
let params = params
.iter()
.cloned()
.map(|p| wasm_val_t::from_val(p))
.collect::<Vec<_>>();
let mut out_results = vec![wasm_val_t::default(); results.len()];
@@ -163,7 +165,7 @@ pub extern "C" fn wasmtime_func_new_with_env(
pub unsafe extern "C" fn wasm_func_call(
wasm_func: &wasm_func_t,
args: *const wasm_val_t,
results: *mut wasm_val_t,
results: *mut MaybeUninit<wasm_val_t>,
) -> *mut wasm_trap_t {
let func = wasm_func.func();
let mut trap = ptr::null_mut();
@@ -186,7 +188,7 @@ pub unsafe extern "C" fn wasmtime_func_call(
func: &wasm_func_t,
args: *const wasm_val_t,
num_args: usize,
results: *mut wasm_val_t,
results: *mut MaybeUninit<wasm_val_t>,
num_results: usize,
trap_ptr: &mut *mut wasm_trap_t,
) -> Option<Box<wasmtime_error_t>> {
@@ -201,7 +203,7 @@ pub unsafe extern "C" fn wasmtime_func_call(
fn _wasmtime_func_call(
func: &wasm_func_t,
args: &[wasm_val_t],
results: &mut [wasm_val_t],
results: &mut [MaybeUninit<wasm_val_t>],
trap_ptr: &mut *mut wasm_trap_t,
) -> Option<Box<wasmtime_error_t>> {
let func = func.func();
@@ -217,8 +219,8 @@ fn _wasmtime_func_call(
let result = panic::catch_unwind(AssertUnwindSafe(|| func.call(&params)));
match result {
Ok(Ok(out)) => {
for (slot, val) in results.iter_mut().zip(out.iter()) {
*slot = wasm_val_t::from_val(val);
for (slot, val) in results.iter_mut().zip(out.into_vec().into_iter()) {
crate::initialize(slot, wasm_val_t::from_val(val));
}
None
}

View File

@@ -1,5 +1,6 @@
use crate::{handle_result, wasmtime_error_t};
use crate::{wasm_extern_t, wasm_globaltype_t, wasm_store_t, wasm_val_t};
use std::mem::MaybeUninit;
use std::ptr;
use wasmtime::{Extern, Global};
@@ -72,8 +73,8 @@ pub extern "C" fn wasm_global_type(g: &wasm_global_t) -> Box<wasm_globaltype_t>
}
#[no_mangle]
pub extern "C" fn wasm_global_get(g: &wasm_global_t, out: &mut wasm_val_t) {
out.set(g.global().get());
pub extern "C" fn wasm_global_get(g: &wasm_global_t, out: &mut MaybeUninit<wasm_val_t>) {
crate::initialize(out, wasm_val_t::from_val(g.global().get()));
}
#[no_mangle]

View File

@@ -63,15 +63,13 @@ pub struct wasm_shared_module_t {
_unused: [u8; 0],
}
struct HostInfoState {
info: *mut std::ffi::c_void,
finalizer: Option<extern "C" fn(arg1: *mut std::ffi::c_void)>,
}
impl Drop for HostInfoState {
fn drop(&mut self) {
if let Some(f) = &self.finalizer {
f(self.info);
}
/// Initialize a `MaybeUninit<T>`
///
/// TODO: Replace calls to this function with
/// https://doc.rust-lang.org/nightly/std/mem/union.MaybeUninit.html#method.write
/// once it is stable.
pub(crate) fn initialize<T>(dst: &mut std::mem::MaybeUninit<T>, val: T) {
unsafe {
std::ptr::write(dst.as_mut_ptr(), val);
}
}

View File

@@ -1,45 +1,144 @@
use crate::wasm_val_t;
use std::any::Any;
use std::mem::MaybeUninit;
use std::os::raw::c_void;
use wasmtime::ExternRef;
use std::ptr;
use wasmtime::{ExternRef, Func, Val};
#[repr(C)]
/// `*mut wasm_ref_t` is a reference type (`externref` or `funcref`), as seen by
/// the C API. Because we do not have a uniform representation for `funcref`s
/// and `externref`s, a `*mut wasm_ref_t` is morally a
/// `Option<Box<Either<ExternRef, Func>>>`.
///
/// A null `*mut wasm_ref_t` is either a null `funcref` or a null `externref`
/// depending on context (e.g. the table's element type that it is going into or
/// coming out of).
///
/// Note: this is not `#[repr(C)]` because it is an opaque type in the header,
/// and only ever referenced as `*mut wasm_ref_t`. This also lets us use a
/// regular, non-`repr(C)` `enum` to define `WasmRefInner`.
#[derive(Clone)]
pub struct wasm_ref_t {
pub(crate) r: Option<ExternRef>,
pub(crate) r: WasmRefInner,
}
#[derive(Clone)]
pub(crate) enum WasmRefInner {
ExternRef(ExternRef),
FuncRef(Func),
}
wasmtime_c_api_macros::declare_own!(wasm_ref_t);
#[no_mangle]
pub extern "C" fn wasm_ref_copy(r: &wasm_ref_t) -> Box<wasm_ref_t> {
Box::new(r.clone())
pub(crate) fn ref_to_val(r: &wasm_ref_t) -> Val {
match &r.r {
WasmRefInner::ExternRef(x) => Val::ExternRef(Some(x.clone())),
WasmRefInner::FuncRef(f) => Val::FuncRef(Some(f.clone())),
}
}
pub(crate) fn val_into_ref(val: Val) -> Option<Box<wasm_ref_t>> {
match val {
Val::ExternRef(Some(x)) => Some(Box::new(wasm_ref_t {
r: WasmRefInner::ExternRef(x),
})),
Val::FuncRef(Some(f)) => Some(Box::new(wasm_ref_t {
r: WasmRefInner::FuncRef(f),
})),
_ => None,
}
}
#[no_mangle]
pub extern "C" fn wasm_ref_same(a: &wasm_ref_t, b: &wasm_ref_t) -> bool {
match (a.r.as_ref(), b.r.as_ref()) {
(Some(a), Some(b)) => a.ptr_eq(b),
pub extern "C" fn wasm_ref_copy(r: Option<&wasm_ref_t>) -> Option<Box<wasm_ref_t>> {
r.map(|r| Box::new(r.clone()))
}
#[no_mangle]
pub extern "C" fn wasm_ref_same(a: Option<&wasm_ref_t>, b: Option<&wasm_ref_t>) -> bool {
match (a.map(|a| &a.r), b.map(|b| &b.r)) {
(Some(WasmRefInner::ExternRef(a)), Some(WasmRefInner::ExternRef(b))) => a.ptr_eq(b),
(None, None) => true,
// Note: we don't support equality for `Func`, so we always return
// `false` for `funcref`s.
_ => false,
}
}
#[no_mangle]
pub extern "C" fn wasm_ref_get_host_info(_ref: &wasm_ref_t) -> *mut c_void {
pub extern "C" fn wasm_ref_get_host_info(_ref: Option<&wasm_ref_t>) -> *mut c_void {
std::ptr::null_mut()
}
#[no_mangle]
pub extern "C" fn wasm_ref_set_host_info(_ref: &wasm_ref_t, _info: *mut c_void) {
pub extern "C" fn wasm_ref_set_host_info(_ref: Option<&wasm_ref_t>, _info: *mut c_void) {
eprintln!("`wasm_ref_set_host_info` is not implemented");
std::process::abort();
}
#[no_mangle]
pub extern "C" fn wasm_ref_set_host_info_with_finalizer(
_ref: &wasm_ref_t,
_ref: Option<&wasm_ref_t>,
_info: *mut c_void,
_finalizer: Option<extern "C" fn(*mut c_void)>,
) {
eprintln!("`wasm_ref_set_host_info_with_finalizer` is not implemented");
std::process::abort();
}
type wasmtime_externref_finalizer_t = extern "C" fn(*mut c_void);
struct CExternRef {
data: *mut c_void,
finalizer: Option<wasmtime_externref_finalizer_t>,
}
impl Drop for CExternRef {
fn drop(&mut self) {
if let Some(f) = self.finalizer {
f(self.data);
}
}
}
#[no_mangle]
pub extern "C" fn wasmtime_externref_new(data: *mut c_void, valp: &mut MaybeUninit<wasm_val_t>) {
wasmtime_externref_new_with_finalizer(data, None, valp)
}
#[no_mangle]
pub extern "C" fn wasmtime_externref_new_with_finalizer(
data: *mut c_void,
finalizer: Option<wasmtime_externref_finalizer_t>,
valp: &mut MaybeUninit<wasm_val_t>,
) {
crate::initialize(
valp,
wasm_val_t::from_val(Val::ExternRef(Some(ExternRef::new(CExternRef {
data,
finalizer,
})))),
);
}
#[no_mangle]
pub extern "C" fn wasmtime_externref_data(
val: &wasm_val_t,
datap: &mut MaybeUninit<*mut c_void>,
) -> bool {
match val.val() {
Val::ExternRef(None) => {
crate::initialize(datap, ptr::null_mut());
true
}
Val::ExternRef(Some(x)) => {
let data = match x.data().downcast_ref::<CExternRef>() {
Some(r) => r.data,
None => x.data() as *const dyn Any as *mut c_void,
};
crate::initialize(datap, data);
true
}
_ => false,
}
}

View File

@@ -1,7 +1,8 @@
use crate::r#ref::{ref_to_val, val_into_ref};
use crate::{handle_result, wasm_func_t, wasm_ref_t, wasmtime_error_t};
use crate::{wasm_extern_t, wasm_store_t, wasm_tabletype_t};
use std::ptr;
use wasmtime::{Extern, Table, Val};
use wasmtime::{Extern, Table, TableType, Val, ValType};
#[derive(Clone)]
#[repr(transparent)]
@@ -29,16 +30,24 @@ impl wasm_table_t {
}
}
fn ref_to_val_for_table(r: Option<&wasm_ref_t>, table_ty: &TableType) -> Val {
r.map_or_else(
|| match table_ty.element() {
ValType::FuncRef => Val::FuncRef(None),
ValType::ExternRef => Val::ExternRef(None),
ty => panic!("unsupported table element type: {:?}", ty),
},
|r| ref_to_val(r),
)
}
#[no_mangle]
pub extern "C" fn wasm_table_new(
store: &wasm_store_t,
tt: &wasm_tabletype_t,
init: Option<Box<wasm_ref_t>>,
init: Option<&wasm_ref_t>,
) -> Option<Box<wasm_table_t>> {
let init: Val = match init {
Some(init) => init.r.into(),
None => Val::FuncRef(None),
};
let init = ref_to_val_for_table(init, &tt.ty().ty);
let table = Table::new(&store.store, tt.ty().ty.clone(), init).ok()?;
Some(Box::new(wasm_table_t {
ext: wasm_extern_t {
@@ -77,11 +86,12 @@ pub extern "C" fn wasm_table_type(t: &wasm_table_t) -> Box<wasm_tabletype_t> {
}
#[no_mangle]
pub extern "C" fn wasm_table_get(t: &wasm_table_t, index: wasm_table_size_t) -> *mut wasm_ref_t {
match t.table().get(index) {
Some(val) => into_funcref(val),
None => into_funcref(Val::FuncRef(None)),
}
pub extern "C" fn wasm_table_get(
t: &wasm_table_t,
index: wasm_table_size_t,
) -> Option<Box<wasm_ref_t>> {
let val = t.table().get(index)?;
Some(val_into_ref(val).unwrap())
}
#[no_mangle]
@@ -108,9 +118,9 @@ pub extern "C" fn wasmtime_funcref_table_get(
pub unsafe extern "C" fn wasm_table_set(
t: &wasm_table_t,
index: wasm_table_size_t,
r: *mut wasm_ref_t,
r: Option<&wasm_ref_t>,
) -> bool {
let val = from_funcref(r);
let val = ref_to_val_for_table(r, &t.table().ty());
t.table().set(index, val).is_ok()
}
@@ -127,26 +137,6 @@ pub extern "C" fn wasmtime_funcref_table_set(
handle_result(t.table().set(index, val), |()| {})
}
fn into_funcref(val: Val) -> *mut wasm_ref_t {
if let Val::FuncRef(None) = val {
return ptr::null_mut();
}
let externref = match val.externref() {
Some(externref) => externref,
None => return ptr::null_mut(),
};
let r = Box::new(wasm_ref_t { r: externref });
Box::into_raw(r)
}
unsafe fn from_funcref(r: *mut wasm_ref_t) -> Val {
if !r.is_null() {
Box::from_raw(r).r.into()
} else {
Val::FuncRef(None)
}
}
#[no_mangle]
pub extern "C" fn wasm_table_size(t: &wasm_table_t) -> wasm_table_size_t {
t.table().size()
@@ -156,9 +146,9 @@ pub extern "C" fn wasm_table_size(t: &wasm_table_t) -> wasm_table_size_t {
pub unsafe extern "C" fn wasm_table_grow(
t: &wasm_table_t,
delta: wasm_table_size_t,
init: *mut wasm_ref_t,
init: Option<&wasm_ref_t>,
) -> bool {
let init = from_funcref(init);
let init = ref_to_val_for_table(init, &t.table().ty());
t.table().grow(delta, init).is_ok()
}

View File

@@ -1,8 +1,10 @@
use crate::r#ref::{ref_to_val, WasmRefInner};
use crate::{from_valtype, into_valtype, wasm_ref_t, wasm_valkind_t, WASM_I32};
use std::mem::MaybeUninit;
use std::ptr;
use wasmtime::{Val, ValType};
#[repr(C)]
#[derive(Copy, Clone)]
pub struct wasm_val_t {
pub kind: wasm_valkind_t,
pub of: wasm_val_union,
@@ -20,6 +22,34 @@ pub union wasm_val_union {
pub ref_: *mut wasm_ref_t,
}
impl Drop for wasm_val_t {
fn drop(&mut self) {
match into_valtype(self.kind) {
ValType::ExternRef => unsafe {
drop(Box::from_raw(self.of.ref_));
},
_ => {}
}
}
}
impl Clone for wasm_val_t {
fn clone(&self) -> Self {
match into_valtype(self.kind) {
ValType::ExternRef => wasm_val_t {
kind: self.kind,
of: wasm_val_union {
ref_: unsafe { Box::into_raw(Box::new((*self.of.ref_).clone())) },
},
},
_ => wasm_val_t {
kind: self.kind,
of: self.of,
},
}
}
}
impl Default for wasm_val_t {
fn default() -> Self {
wasm_val_t {
@@ -30,46 +60,52 @@ impl Default for wasm_val_t {
}
impl wasm_val_t {
pub fn from_val(val: &Val) -> wasm_val_t {
pub fn from_val(val: Val) -> wasm_val_t {
match val {
Val::I32(i) => wasm_val_t {
kind: from_valtype(&ValType::I32),
of: wasm_val_union { i32: *i },
of: wasm_val_union { i32: i },
},
Val::I64(i) => wasm_val_t {
kind: from_valtype(&ValType::I64),
of: wasm_val_union { i64: *i },
of: wasm_val_union { i64: i },
},
Val::F32(f) => wasm_val_t {
kind: from_valtype(&ValType::F32),
of: wasm_val_union { u32: *f },
of: wasm_val_union { u32: f },
},
Val::F64(f) => wasm_val_t {
kind: from_valtype(&ValType::F64),
of: wasm_val_union { u64: *f },
of: wasm_val_union { u64: f },
},
Val::ExternRef(None) => wasm_val_t {
kind: from_valtype(&ValType::ExternRef),
of: wasm_val_union {
ref_: ptr::null_mut(),
},
},
Val::ExternRef(Some(r)) => wasm_val_t {
kind: from_valtype(&ValType::ExternRef),
of: wasm_val_union {
ref_: Box::into_raw(Box::new(wasm_ref_t {
r: WasmRefInner::ExternRef(r),
})),
},
},
Val::FuncRef(None) => wasm_val_t {
kind: from_valtype(&ValType::FuncRef),
of: wasm_val_union {
ref_: ptr::null_mut(),
},
},
Val::FuncRef(Some(f)) => wasm_val_t {
kind: from_valtype(&ValType::FuncRef),
of: wasm_val_union {
ref_: Box::into_raw(Box::new(wasm_ref_t {
r: WasmRefInner::FuncRef(f),
})),
},
},
_ => unimplemented!("wasm_val_t::from_val {:?}", val),
}
}
pub fn set(&mut self, val: Val) {
match val {
Val::I32(i) => {
self.kind = from_valtype(&ValType::I32);
self.of = wasm_val_union { i32: i };
}
Val::I64(i) => {
self.kind = from_valtype(&ValType::I64);
self.of = wasm_val_union { i64: i };
}
Val::F32(f) => {
self.kind = from_valtype(&ValType::F32);
self.of = wasm_val_union { u32: f };
}
Val::F64(f) => {
self.kind = from_valtype(&ValType::F64);
self.of = wasm_val_union { u64: f };
}
_ => unimplemented!("wasm_val_t::from_val {:?}", val),
}
}
@@ -80,20 +116,29 @@ impl wasm_val_t {
ValType::I64 => Val::from(unsafe { self.of.i64 }),
ValType::F32 => Val::from(unsafe { self.of.f32 }),
ValType::F64 => Val::from(unsafe { self.of.f64 }),
ValType::ExternRef | ValType::FuncRef => ref_to_val(unsafe { &*self.of.ref_ }),
_ => unimplemented!("wasm_val_t::val {:?}", self.kind),
}
}
}
#[no_mangle]
pub unsafe extern "C" fn wasm_val_copy(out: *mut wasm_val_t, source: &wasm_val_t) {
*out = match into_valtype(source.kind) {
ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 => *source,
_ => unimplemented!("wasm_val_copy arg"),
};
pub unsafe extern "C" fn wasm_val_copy(out: &mut MaybeUninit<wasm_val_t>, source: &wasm_val_t) {
crate::initialize(
out,
match into_valtype(source.kind) {
ValType::I32
| ValType::I64
| ValType::F32
| ValType::F64
| ValType::ExternRef
| ValType::FuncRef => source.clone(),
_ => unimplemented!("wasm_val_copy arg"),
},
);
}
#[no_mangle]
pub extern "C" fn wasm_val_delete(_val: &mut wasm_val_t) {
// currently we only support integers/floats which need no deletion
pub unsafe extern "C" fn wasm_val_delete(val: *mut wasm_val_t) {
ptr::drop_in_place(val);
}

View File

@@ -1,148 +0,0 @@
use std::env;
use std::fs;
use std::path::Path;
use std::process::Command;
use std::sync::Once;
fn run_c_example(name: &'static str, expected_out: &str) {
// Windows requires different `cc` flags and I'm not sure what they
// are. Also we need a way to shepherd the current host target to the `cc`
// invocation but `cargo` only defines the `TARGET` environment variable for
// build scripts, not tests. Therefore, we just make these tests specific to
// bog standard x64 linux. This should run in CI, at least!
if cfg!(not(all(
target_arch = "x86_64",
target_os = "linux",
target_env = "gnu"
))) {
eprintln!("This test is only enabled for the `x86_64-unknown-linux-gnu` target");
return;
}
let pkg_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
// Make sure we've built `libwasmtime.a` with the `wat` feature enabled
// so that we have the `wasmtime_wat2wasm` function.
static BUILD_LIBWASMTIME: Once = Once::new();
BUILD_LIBWASMTIME.call_once(|| {
let status = Command::new("cargo")
.args(&["build", "-p", "wasmtime-c-api", "--features", "wat"])
.current_dir(pkg_dir)
.status()
.expect("should run `cargo build` OK");
assert!(status.success());
});
let examples_dir = pkg_dir
// Pop `c-api`.
.join("..")
// Pop `crates`.
.join("..")
.join("examples");
let include_dir = pkg_dir.join("include");
let wasm_c_api_include_dir = pkg_dir.join("wasm-c-api").join("include");
let out_dir = pkg_dir.join("..").join("..").join("target").join("debug");
let c_examples_dir = out_dir.join("c-examples");
fs::create_dir_all(&c_examples_dir).unwrap();
let libwasmtime = out_dir.join("libwasmtime.a");
assert!(libwasmtime.exists());
let status = Command::new(env::var("CC").unwrap_or("gcc".into()))
.arg(examples_dir.join(name).with_extension("c"))
.arg(libwasmtime)
.arg(format!("-I{}", include_dir.display()))
.arg(format!("-I{}", wasm_c_api_include_dir.display()))
.arg("-lpthread")
.arg("-ldl")
.arg("-lm")
.arg("-lrt")
.current_dir(&examples_dir)
.arg("-o")
.arg(c_examples_dir.join(name))
.status()
.expect("should spawn CC ok");
assert!(status.success());
assert!(c_examples_dir.join(name).exists());
let output = Command::new(c_examples_dir.join(name))
.current_dir(pkg_dir.join("..").join(".."))
.output()
.expect("should spawn C example OK");
assert!(
output.status.success(),
"failed to execute the C example '{}': {}",
name,
String::from_utf8_lossy(&output.stderr),
);
let actual_stdout =
String::from_utf8(output.stdout).expect("C example's output should be utf-8");
assert_eq!(
actual_stdout, expected_out,
"unexpected stdout from example",
);
}
#[test]
fn test_run_hello_example() {
run_c_example(
"hello",
"Initializing...\n\
Compiling module...\n\
Creating callback...\n\
Instantiating module...\n\
Extracting export...\n\
Calling export...\n\
Calling back...\n\
> Hello World!\n\
All finished!\n",
);
}
#[test]
fn test_run_memory_example() {
run_c_example(
"memory",
"Initializing...\n\
Compiling module...\n\
Instantiating module...\n\
Extracting exports...\n\
Checking memory...\n\
Mutating memory...\n\
Growing memory...\n\
Creating stand-alone memory...\n\
Shutting down...\n\
Done.\n",
);
}
#[test]
fn test_run_linking_example() {
run_c_example("linking", "Hello, world!\n");
}
#[test]
fn test_run_multi_example() {
run_c_example(
"multi",
"Initializing...\n\
Compiling module...\n\
Creating callback...\n\
Instantiating module...\n\
Extracting export...\n\
Calling export...\n\
Calling back...\n\
> 1 2\n\
\n\
Printing result...\n\
> 2 1\n\
Shutting down...\n\
Done.\n",
);
}
#[test]
fn test_run_gcd_example() {
run_c_example("gcd", "gcd(6, 27) = 3\n");
}

View File

@@ -6,4 +6,5 @@ edition = "2018"
publish = false
[dependencies]
anyhow = "1.0.31"
cc = "1.0"

View File

@@ -1,26 +1,25 @@
use anyhow::Context;
use std::collections::BTreeSet;
use std::process::Command;
fn main() {
fn main() -> anyhow::Result<()> {
let example_to_run = std::env::args().nth(1);
let examples = std::fs::read_dir("examples").unwrap();
let examples = examples
.filter_map(|e| {
let e = e.unwrap();
let path = e.path();
let dir = e.metadata().unwrap().is_dir();
if let Some("wat") = path.extension().and_then(|s| s.to_str()) {
return None;
}
let mut examples = BTreeSet::new();
for e in std::fs::read_dir("examples")? {
let e = e?;
let path = e.path();
let dir = e.metadata()?.is_dir();
if let Some("wat") = path.extension().and_then(|s| s.to_str()) {
continue;
}
Some((path.file_stem().unwrap().to_str().unwrap().to_owned(), dir))
})
.collect::<BTreeSet<_>>();
examples.insert((path.file_stem().unwrap().to_str().unwrap().to_owned(), dir));
}
println!("======== Building libwasmtime.a ===========");
run(Command::new("cargo")
.args(&["build"])
.current_dir("crates/c-api"));
.current_dir("crates/c-api"))?;
for (example, is_dir) in examples {
if example == "README" {
@@ -43,13 +42,13 @@ fn main() {
.arg("-p")
.arg(format!("example-{}-wasm", example))
.arg("--target")
.arg(target));
.arg(target))?;
}
println!("======== Rust example `{}` ============", example);
run(Command::new("cargo")
.arg("run")
.arg("--example")
.arg(&example));
.arg(&example))?;
println!("======== C/C++ example `{}` ============", example);
for extension in ["c", "cc"].iter() {
@@ -85,26 +84,34 @@ fn main() {
.arg("ntdll.lib")
.arg("shell32.lib")
.arg("ole32.lib");
"./main.exe"
if is_dir {
"main.exe".to_string()
} else {
format!("./{}.exe", example)
}
} else {
cmd.arg("target/debug/libwasmtime.a").arg("-o").arg("foo");
"./foo"
"./foo".to_string()
};
if cfg!(target_os = "linux") {
cmd.arg("-lpthread").arg("-ldl").arg("-lm");
}
run(&mut cmd);
run(&mut cmd)?;
run(&mut Command::new(exe));
run(&mut Command::new(exe))?;
}
}
Ok(())
}
fn run(cmd: &mut Command) {
let s = cmd.status().unwrap();
if !s.success() {
eprintln!("failed to run {:?}", cmd);
eprintln!("status: {}", s);
std::process::exit(1);
}
fn run(cmd: &mut Command) -> anyhow::Result<()> {
(|| -> anyhow::Result<()> {
let s = cmd.status()?;
if !s.success() {
anyhow::bail!("Exited with failure status: {}", s);
}
Ok(())
})()
.with_context(|| format!("failed to run `{:?}`", cmd))
}