Merge pull request #1996 from fitzgen/ref-types-in-c-api
Support reference types in the C API
This commit is contained in:
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@@ -231,6 +231,8 @@ jobs:
|
|||||||
|
|
||||||
# Ensure all our examples build and execute
|
# Ensure all our examples build and execute
|
||||||
- run: cargo run -p run-examples
|
- run: cargo run -p run-examples
|
||||||
|
env:
|
||||||
|
RUST_BACKTRACE: 1
|
||||||
|
|
||||||
# Build and test all features except for lightbeam
|
# Build and test all features except for lightbeam
|
||||||
- run: |
|
- run: |
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -15,3 +15,4 @@ rusty-tags.*
|
|||||||
tags
|
tags
|
||||||
target
|
target
|
||||||
.z3-trace
|
.z3-trace
|
||||||
|
foo
|
||||||
|
|||||||
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1743,6 +1743,7 @@ dependencies = [
|
|||||||
name = "run-examples"
|
name = "run-examples"
|
||||||
version = "0.18.0"
|
version = "0.18.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -1147,19 +1147,22 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* \struct wasm_ref_t
|
* \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
|
* \typedef wasm_ref_t
|
||||||
* \brief Convenience alias for #wasm_ref_t
|
* \brief Convenience alias for #wasm_ref_t
|
||||||
*
|
*
|
||||||
* \fn void wasm_ref_delete(own wasm_ref_t *v);
|
* \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 *)
|
* \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 *)
|
* \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 *);
|
* \fn void* wasm_ref_get_host_info(const wasm_ref_t *);
|
||||||
* \brief Unimplemented in Wasmtime, always returns `NULL`.
|
* \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
|
* 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.
|
* 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
|
* > Note: to avoid the UB associated with passing the wrong number of results
|
||||||
* > or parameters by accident, or to distinguish between traps and other
|
* > or parameters by accident, or to distinguish between traps and other
|
||||||
* > errors, it's recommended to use #wasmtime_func_call.
|
* > 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
|
* 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.
|
* 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.
|
* > 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 *);
|
* \fn wasm_tabletype_t *wasm_table_type(const wasm_table_t *);
|
||||||
* \brief Returns the type of this table.
|
* \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
|
* Attempts to get a value at an index in this table. This function returns
|
||||||
* `NULL` if the index is out of bounds.
|
* `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
|
* > Note: for funcref tables you can use #wasmtime_funcref_table_get to learn
|
||||||
* > about out-of-bounds errors.
|
* > 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 *);
|
* \fn void wasm_table_set(wasm_table_t *, wasm_table_size_t index, wasm_ref_t *);
|
||||||
* \brief Sets an element in this table.
|
* \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 comes from a different store than the table provided.
|
||||||
* * The #wasm_ref_t does not have an appropriate type to store in this table.
|
* * 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
|
* > Note: for funcref tables you can use #wasmtime_funcref_table_set to learn
|
||||||
* > about errors.
|
* > 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 *);
|
* \fn wasm_table_size_t wasm_table_size(const wasm_table_t *);
|
||||||
* \brief Gets the current size, in elements, of this table.
|
* \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 comes from a different store than the table provided.
|
||||||
* * The #wasm_ref_t does not have an appropriate type to store in this table.
|
* * 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.
|
* > 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.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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
|
* The `trap` pointer cannot be `NULL`. The `args` and `results` pointers may be
|
||||||
* `NULL` if the corresponding length is zero.
|
* `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_API_EXTERN own wasmtime_error_t *wasmtime_func_call(
|
||||||
wasm_func_t *func,
|
wasm_func_t *func,
|
||||||
@@ -833,6 +836,63 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_funcref_table_grow(
|
|||||||
wasm_table_size_t *prev_size
|
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
|
#undef own
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@@ -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 crate::{wasm_name_t, wasm_trap_t, wasmtime_error_t};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
use std::panic::{self, AssertUnwindSafe};
|
use std::panic::{self, AssertUnwindSafe};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::str;
|
use std::str;
|
||||||
@@ -89,6 +90,7 @@ fn create_function(
|
|||||||
let func = Func::new(store, ty, move |caller, params, results| {
|
let func = Func::new(store, ty, move |caller, params, results| {
|
||||||
let params = params
|
let params = params
|
||||||
.iter()
|
.iter()
|
||||||
|
.cloned()
|
||||||
.map(|p| wasm_val_t::from_val(p))
|
.map(|p| wasm_val_t::from_val(p))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let mut out_results = vec![wasm_val_t::default(); results.len()];
|
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(
|
pub unsafe extern "C" fn wasm_func_call(
|
||||||
wasm_func: &wasm_func_t,
|
wasm_func: &wasm_func_t,
|
||||||
args: *const wasm_val_t,
|
args: *const wasm_val_t,
|
||||||
results: *mut wasm_val_t,
|
results: *mut MaybeUninit<wasm_val_t>,
|
||||||
) -> *mut wasm_trap_t {
|
) -> *mut wasm_trap_t {
|
||||||
let func = wasm_func.func();
|
let func = wasm_func.func();
|
||||||
let mut trap = ptr::null_mut();
|
let mut trap = ptr::null_mut();
|
||||||
@@ -186,7 +188,7 @@ pub unsafe extern "C" fn wasmtime_func_call(
|
|||||||
func: &wasm_func_t,
|
func: &wasm_func_t,
|
||||||
args: *const wasm_val_t,
|
args: *const wasm_val_t,
|
||||||
num_args: usize,
|
num_args: usize,
|
||||||
results: *mut wasm_val_t,
|
results: *mut MaybeUninit<wasm_val_t>,
|
||||||
num_results: usize,
|
num_results: usize,
|
||||||
trap_ptr: &mut *mut wasm_trap_t,
|
trap_ptr: &mut *mut wasm_trap_t,
|
||||||
) -> Option<Box<wasmtime_error_t>> {
|
) -> Option<Box<wasmtime_error_t>> {
|
||||||
@@ -201,7 +203,7 @@ pub unsafe extern "C" fn wasmtime_func_call(
|
|||||||
fn _wasmtime_func_call(
|
fn _wasmtime_func_call(
|
||||||
func: &wasm_func_t,
|
func: &wasm_func_t,
|
||||||
args: &[wasm_val_t],
|
args: &[wasm_val_t],
|
||||||
results: &mut [wasm_val_t],
|
results: &mut [MaybeUninit<wasm_val_t>],
|
||||||
trap_ptr: &mut *mut wasm_trap_t,
|
trap_ptr: &mut *mut wasm_trap_t,
|
||||||
) -> Option<Box<wasmtime_error_t>> {
|
) -> Option<Box<wasmtime_error_t>> {
|
||||||
let func = func.func();
|
let func = func.func();
|
||||||
@@ -217,8 +219,8 @@ fn _wasmtime_func_call(
|
|||||||
let result = panic::catch_unwind(AssertUnwindSafe(|| func.call(¶ms)));
|
let result = panic::catch_unwind(AssertUnwindSafe(|| func.call(¶ms)));
|
||||||
match result {
|
match result {
|
||||||
Ok(Ok(out)) => {
|
Ok(Ok(out)) => {
|
||||||
for (slot, val) in results.iter_mut().zip(out.iter()) {
|
for (slot, val) in results.iter_mut().zip(out.into_vec().into_iter()) {
|
||||||
*slot = wasm_val_t::from_val(val);
|
crate::initialize(slot, wasm_val_t::from_val(val));
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use crate::{handle_result, wasmtime_error_t};
|
use crate::{handle_result, wasmtime_error_t};
|
||||||
use crate::{wasm_extern_t, wasm_globaltype_t, wasm_store_t, wasm_val_t};
|
use crate::{wasm_extern_t, wasm_globaltype_t, wasm_store_t, wasm_val_t};
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use wasmtime::{Extern, Global};
|
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]
|
#[no_mangle]
|
||||||
pub extern "C" fn wasm_global_get(g: &wasm_global_t, out: &mut wasm_val_t) {
|
pub extern "C" fn wasm_global_get(g: &wasm_global_t, out: &mut MaybeUninit<wasm_val_t>) {
|
||||||
out.set(g.global().get());
|
crate::initialize(out, wasm_val_t::from_val(g.global().get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
|||||||
@@ -63,15 +63,13 @@ pub struct wasm_shared_module_t {
|
|||||||
_unused: [u8; 0],
|
_unused: [u8; 0],
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HostInfoState {
|
/// Initialize a `MaybeUninit<T>`
|
||||||
info: *mut std::ffi::c_void,
|
///
|
||||||
finalizer: Option<extern "C" fn(arg1: *mut std::ffi::c_void)>,
|
/// TODO: Replace calls to this function with
|
||||||
}
|
/// https://doc.rust-lang.org/nightly/std/mem/union.MaybeUninit.html#method.write
|
||||||
|
/// once it is stable.
|
||||||
impl Drop for HostInfoState {
|
pub(crate) fn initialize<T>(dst: &mut std::mem::MaybeUninit<T>, val: T) {
|
||||||
fn drop(&mut self) {
|
unsafe {
|
||||||
if let Some(f) = &self.finalizer {
|
std::ptr::write(dst.as_mut_ptr(), val);
|
||||||
f(self.info);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,45 +1,144 @@
|
|||||||
|
use crate::wasm_val_t;
|
||||||
|
use std::any::Any;
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
use std::os::raw::c_void;
|
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)]
|
#[derive(Clone)]
|
||||||
pub struct wasm_ref_t {
|
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);
|
wasmtime_c_api_macros::declare_own!(wasm_ref_t);
|
||||||
|
|
||||||
#[no_mangle]
|
pub(crate) fn ref_to_val(r: &wasm_ref_t) -> Val {
|
||||||
pub extern "C" fn wasm_ref_copy(r: &wasm_ref_t) -> Box<wasm_ref_t> {
|
match &r.r {
|
||||||
Box::new(r.clone())
|
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]
|
#[no_mangle]
|
||||||
pub extern "C" fn wasm_ref_same(a: &wasm_ref_t, b: &wasm_ref_t) -> bool {
|
pub extern "C" fn wasm_ref_copy(r: Option<&wasm_ref_t>) -> Option<Box<wasm_ref_t>> {
|
||||||
match (a.r.as_ref(), b.r.as_ref()) {
|
r.map(|r| Box::new(r.clone()))
|
||||||
(Some(a), Some(b)) => a.ptr_eq(b),
|
}
|
||||||
|
|
||||||
|
#[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,
|
(None, None) => true,
|
||||||
|
// Note: we don't support equality for `Func`, so we always return
|
||||||
|
// `false` for `funcref`s.
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[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()
|
std::ptr::null_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[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");
|
eprintln!("`wasm_ref_set_host_info` is not implemented");
|
||||||
std::process::abort();
|
std::process::abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn wasm_ref_set_host_info_with_finalizer(
|
pub extern "C" fn wasm_ref_set_host_info_with_finalizer(
|
||||||
_ref: &wasm_ref_t,
|
_ref: Option<&wasm_ref_t>,
|
||||||
_info: *mut c_void,
|
_info: *mut c_void,
|
||||||
_finalizer: Option<extern "C" fn(*mut c_void)>,
|
_finalizer: Option<extern "C" fn(*mut c_void)>,
|
||||||
) {
|
) {
|
||||||
eprintln!("`wasm_ref_set_host_info_with_finalizer` is not implemented");
|
eprintln!("`wasm_ref_set_host_info_with_finalizer` is not implemented");
|
||||||
std::process::abort();
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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::{handle_result, wasm_func_t, wasm_ref_t, wasmtime_error_t};
|
||||||
use crate::{wasm_extern_t, wasm_store_t, wasm_tabletype_t};
|
use crate::{wasm_extern_t, wasm_store_t, wasm_tabletype_t};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use wasmtime::{Extern, Table, Val};
|
use wasmtime::{Extern, Table, TableType, Val, ValType};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
#[repr(transparent)]
|
#[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]
|
#[no_mangle]
|
||||||
pub extern "C" fn wasm_table_new(
|
pub extern "C" fn wasm_table_new(
|
||||||
store: &wasm_store_t,
|
store: &wasm_store_t,
|
||||||
tt: &wasm_tabletype_t,
|
tt: &wasm_tabletype_t,
|
||||||
init: Option<Box<wasm_ref_t>>,
|
init: Option<&wasm_ref_t>,
|
||||||
) -> Option<Box<wasm_table_t>> {
|
) -> Option<Box<wasm_table_t>> {
|
||||||
let init: Val = match init {
|
let init = ref_to_val_for_table(init, &tt.ty().ty);
|
||||||
Some(init) => init.r.into(),
|
|
||||||
None => Val::FuncRef(None),
|
|
||||||
};
|
|
||||||
let table = Table::new(&store.store, tt.ty().ty.clone(), init).ok()?;
|
let table = Table::new(&store.store, tt.ty().ty.clone(), init).ok()?;
|
||||||
Some(Box::new(wasm_table_t {
|
Some(Box::new(wasm_table_t {
|
||||||
ext: wasm_extern_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]
|
#[no_mangle]
|
||||||
pub extern "C" fn wasm_table_get(t: &wasm_table_t, index: wasm_table_size_t) -> *mut wasm_ref_t {
|
pub extern "C" fn wasm_table_get(
|
||||||
match t.table().get(index) {
|
t: &wasm_table_t,
|
||||||
Some(val) => into_funcref(val),
|
index: wasm_table_size_t,
|
||||||
None => into_funcref(Val::FuncRef(None)),
|
) -> Option<Box<wasm_ref_t>> {
|
||||||
}
|
let val = t.table().get(index)?;
|
||||||
|
Some(val_into_ref(val).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@@ -108,9 +118,9 @@ pub extern "C" fn wasmtime_funcref_table_get(
|
|||||||
pub unsafe extern "C" fn wasm_table_set(
|
pub unsafe extern "C" fn wasm_table_set(
|
||||||
t: &wasm_table_t,
|
t: &wasm_table_t,
|
||||||
index: wasm_table_size_t,
|
index: wasm_table_size_t,
|
||||||
r: *mut wasm_ref_t,
|
r: Option<&wasm_ref_t>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let val = from_funcref(r);
|
let val = ref_to_val_for_table(r, &t.table().ty());
|
||||||
t.table().set(index, val).is_ok()
|
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), |()| {})
|
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]
|
#[no_mangle]
|
||||||
pub extern "C" fn wasm_table_size(t: &wasm_table_t) -> wasm_table_size_t {
|
pub extern "C" fn wasm_table_size(t: &wasm_table_t) -> wasm_table_size_t {
|
||||||
t.table().size()
|
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(
|
pub unsafe extern "C" fn wasm_table_grow(
|
||||||
t: &wasm_table_t,
|
t: &wasm_table_t,
|
||||||
delta: wasm_table_size_t,
|
delta: wasm_table_size_t,
|
||||||
init: *mut wasm_ref_t,
|
init: Option<&wasm_ref_t>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let init = from_funcref(init);
|
let init = ref_to_val_for_table(init, &t.table().ty());
|
||||||
t.table().grow(delta, init).is_ok()
|
t.table().grow(delta, init).is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 crate::{from_valtype, into_valtype, wasm_ref_t, wasm_valkind_t, WASM_I32};
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
|
use std::ptr;
|
||||||
use wasmtime::{Val, ValType};
|
use wasmtime::{Val, ValType};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct wasm_val_t {
|
pub struct wasm_val_t {
|
||||||
pub kind: wasm_valkind_t,
|
pub kind: wasm_valkind_t,
|
||||||
pub of: wasm_val_union,
|
pub of: wasm_val_union,
|
||||||
@@ -20,6 +22,34 @@ pub union wasm_val_union {
|
|||||||
pub ref_: *mut wasm_ref_t,
|
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 {
|
impl Default for wasm_val_t {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
wasm_val_t {
|
wasm_val_t {
|
||||||
@@ -30,46 +60,52 @@ impl Default for wasm_val_t {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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 {
|
match val {
|
||||||
Val::I32(i) => wasm_val_t {
|
Val::I32(i) => wasm_val_t {
|
||||||
kind: from_valtype(&ValType::I32),
|
kind: from_valtype(&ValType::I32),
|
||||||
of: wasm_val_union { i32: *i },
|
of: wasm_val_union { i32: i },
|
||||||
},
|
},
|
||||||
Val::I64(i) => wasm_val_t {
|
Val::I64(i) => wasm_val_t {
|
||||||
kind: from_valtype(&ValType::I64),
|
kind: from_valtype(&ValType::I64),
|
||||||
of: wasm_val_union { i64: *i },
|
of: wasm_val_union { i64: i },
|
||||||
},
|
},
|
||||||
Val::F32(f) => wasm_val_t {
|
Val::F32(f) => wasm_val_t {
|
||||||
kind: from_valtype(&ValType::F32),
|
kind: from_valtype(&ValType::F32),
|
||||||
of: wasm_val_union { u32: *f },
|
of: wasm_val_union { u32: f },
|
||||||
},
|
},
|
||||||
Val::F64(f) => wasm_val_t {
|
Val::F64(f) => wasm_val_t {
|
||||||
kind: from_valtype(&ValType::F64),
|
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),
|
_ => unimplemented!("wasm_val_t::from_val {:?}", val),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,20 +116,29 @@ impl wasm_val_t {
|
|||||||
ValType::I64 => Val::from(unsafe { self.of.i64 }),
|
ValType::I64 => Val::from(unsafe { self.of.i64 }),
|
||||||
ValType::F32 => Val::from(unsafe { self.of.f32 }),
|
ValType::F32 => Val::from(unsafe { self.of.f32 }),
|
||||||
ValType::F64 => Val::from(unsafe { self.of.f64 }),
|
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),
|
_ => unimplemented!("wasm_val_t::val {:?}", self.kind),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn wasm_val_copy(out: *mut wasm_val_t, source: &wasm_val_t) {
|
pub unsafe extern "C" fn wasm_val_copy(out: &mut MaybeUninit<wasm_val_t>, source: &wasm_val_t) {
|
||||||
*out = match into_valtype(source.kind) {
|
crate::initialize(
|
||||||
ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 => *source,
|
out,
|
||||||
_ => unimplemented!("wasm_val_copy arg"),
|
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]
|
#[no_mangle]
|
||||||
pub extern "C" fn wasm_val_delete(_val: &mut wasm_val_t) {
|
pub unsafe extern "C" fn wasm_val_delete(val: *mut wasm_val_t) {
|
||||||
// currently we only support integers/floats which need no deletion
|
ptr::drop_in_place(val);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
|
||||||
}
|
|
||||||
@@ -6,4 +6,5 @@ edition = "2018"
|
|||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = "1.0.31"
|
||||||
cc = "1.0"
|
cc = "1.0"
|
||||||
|
|||||||
@@ -1,26 +1,25 @@
|
|||||||
|
use anyhow::Context;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> anyhow::Result<()> {
|
||||||
let example_to_run = std::env::args().nth(1);
|
let example_to_run = std::env::args().nth(1);
|
||||||
let examples = std::fs::read_dir("examples").unwrap();
|
let mut examples = BTreeSet::new();
|
||||||
let examples = examples
|
for e in std::fs::read_dir("examples")? {
|
||||||
.filter_map(|e| {
|
let e = e?;
|
||||||
let e = e.unwrap();
|
let path = e.path();
|
||||||
let path = e.path();
|
let dir = e.metadata()?.is_dir();
|
||||||
let dir = e.metadata().unwrap().is_dir();
|
if let Some("wat") = path.extension().and_then(|s| s.to_str()) {
|
||||||
if let Some("wat") = path.extension().and_then(|s| s.to_str()) {
|
continue;
|
||||||
return None;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Some((path.file_stem().unwrap().to_str().unwrap().to_owned(), dir))
|
examples.insert((path.file_stem().unwrap().to_str().unwrap().to_owned(), dir));
|
||||||
})
|
}
|
||||||
.collect::<BTreeSet<_>>();
|
|
||||||
|
|
||||||
println!("======== Building libwasmtime.a ===========");
|
println!("======== Building libwasmtime.a ===========");
|
||||||
run(Command::new("cargo")
|
run(Command::new("cargo")
|
||||||
.args(&["build"])
|
.args(&["build"])
|
||||||
.current_dir("crates/c-api"));
|
.current_dir("crates/c-api"))?;
|
||||||
|
|
||||||
for (example, is_dir) in examples {
|
for (example, is_dir) in examples {
|
||||||
if example == "README" {
|
if example == "README" {
|
||||||
@@ -43,13 +42,13 @@ fn main() {
|
|||||||
.arg("-p")
|
.arg("-p")
|
||||||
.arg(format!("example-{}-wasm", example))
|
.arg(format!("example-{}-wasm", example))
|
||||||
.arg("--target")
|
.arg("--target")
|
||||||
.arg(target));
|
.arg(target))?;
|
||||||
}
|
}
|
||||||
println!("======== Rust example `{}` ============", example);
|
println!("======== Rust example `{}` ============", example);
|
||||||
run(Command::new("cargo")
|
run(Command::new("cargo")
|
||||||
.arg("run")
|
.arg("run")
|
||||||
.arg("--example")
|
.arg("--example")
|
||||||
.arg(&example));
|
.arg(&example))?;
|
||||||
|
|
||||||
println!("======== C/C++ example `{}` ============", example);
|
println!("======== C/C++ example `{}` ============", example);
|
||||||
for extension in ["c", "cc"].iter() {
|
for extension in ["c", "cc"].iter() {
|
||||||
@@ -85,26 +84,34 @@ fn main() {
|
|||||||
.arg("ntdll.lib")
|
.arg("ntdll.lib")
|
||||||
.arg("shell32.lib")
|
.arg("shell32.lib")
|
||||||
.arg("ole32.lib");
|
.arg("ole32.lib");
|
||||||
"./main.exe"
|
if is_dir {
|
||||||
|
"main.exe".to_string()
|
||||||
|
} else {
|
||||||
|
format!("./{}.exe", example)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
cmd.arg("target/debug/libwasmtime.a").arg("-o").arg("foo");
|
cmd.arg("target/debug/libwasmtime.a").arg("-o").arg("foo");
|
||||||
"./foo"
|
"./foo".to_string()
|
||||||
};
|
};
|
||||||
if cfg!(target_os = "linux") {
|
if cfg!(target_os = "linux") {
|
||||||
cmd.arg("-lpthread").arg("-ldl").arg("-lm");
|
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) {
|
fn run(cmd: &mut Command) -> anyhow::Result<()> {
|
||||||
let s = cmd.status().unwrap();
|
(|| -> anyhow::Result<()> {
|
||||||
if !s.success() {
|
let s = cmd.status()?;
|
||||||
eprintln!("failed to run {:?}", cmd);
|
if !s.success() {
|
||||||
eprintln!("status: {}", s);
|
anyhow::bail!("Exited with failure status: {}", s);
|
||||||
std::process::exit(1);
|
}
|
||||||
}
|
Ok(())
|
||||||
|
})()
|
||||||
|
.with_context(|| format!("failed to run `{:?}`", cmd))
|
||||||
}
|
}
|
||||||
|
|||||||
181
examples/externref.c
Normal file
181
examples/externref.c
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
Example of using `externref` values.
|
||||||
|
|
||||||
|
You can compile and run this example on Linux with:
|
||||||
|
|
||||||
|
cargo build --release -p wasmtime
|
||||||
|
cc examples/externref.c \
|
||||||
|
-I crates/c-api/include \
|
||||||
|
-I crates/c-api/wasm-c-api/include \
|
||||||
|
target/release/libwasmtime.a \
|
||||||
|
-lpthread -ldl -lm \
|
||||||
|
-o externref
|
||||||
|
./externref
|
||||||
|
|
||||||
|
Note that on Windows and macOS the command will be similar, but you'll need
|
||||||
|
to tweak the `-lpthread` and such annotations as well as the name of the
|
||||||
|
`libwasmtime.a` file on Windows.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <wasm.h>
|
||||||
|
#include <wasmtime.h>
|
||||||
|
|
||||||
|
static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap);
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int ret = 0;
|
||||||
|
bool ok = true;
|
||||||
|
// Create a new configuration with Wasm reference types enabled.
|
||||||
|
printf("Initializing...\n");
|
||||||
|
wasm_config_t *config = wasm_config_new();
|
||||||
|
assert(config != NULL);
|
||||||
|
wasmtime_config_wasm_reference_types_set(config, true);
|
||||||
|
|
||||||
|
// Create an *engine*, which is a compilation context, with our configured
|
||||||
|
// options.
|
||||||
|
wasm_engine_t *engine = wasm_engine_new_with_config(config);
|
||||||
|
assert(engine != NULL);
|
||||||
|
|
||||||
|
// With an engine we can create a *store* which is a long-lived group of wasm
|
||||||
|
// modules.
|
||||||
|
wasm_store_t *store = wasm_store_new(engine);
|
||||||
|
assert(store != NULL);
|
||||||
|
|
||||||
|
// Read our input file, which in this case is a wasm text file.
|
||||||
|
FILE* file = fopen("examples/externref.wat", "r");
|
||||||
|
assert(file != NULL);
|
||||||
|
fseek(file, 0L, SEEK_END);
|
||||||
|
size_t file_size = ftell(file);
|
||||||
|
fseek(file, 0L, SEEK_SET);
|
||||||
|
wasm_byte_vec_t wat;
|
||||||
|
wasm_byte_vec_new_uninitialized(&wat, file_size);
|
||||||
|
assert(fread(wat.data, file_size, 1, file) == 1);
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
// Parse the wat into the binary wasm format
|
||||||
|
wasm_byte_vec_t wasm;
|
||||||
|
wasmtime_error_t *error = wasmtime_wat2wasm(&wat, &wasm);
|
||||||
|
if (error != NULL)
|
||||||
|
exit_with_error("failed to parse wat", error, NULL);
|
||||||
|
wasm_byte_vec_delete(&wat);
|
||||||
|
|
||||||
|
// Now that we've got our binary webassembly we can compile our module.
|
||||||
|
printf("Compiling module...\n");
|
||||||
|
wasm_module_t *module = NULL;
|
||||||
|
error = wasmtime_module_new(store, &wasm, &module);
|
||||||
|
wasm_byte_vec_delete(&wasm);
|
||||||
|
if (error != NULL)
|
||||||
|
exit_with_error("failed to compile module", error, NULL);
|
||||||
|
|
||||||
|
// Instantiate the module.
|
||||||
|
printf("Instantiating module...\n");
|
||||||
|
wasm_trap_t *trap = NULL;
|
||||||
|
wasm_instance_t *instance = NULL;
|
||||||
|
error = wasmtime_instance_new(store, module, NULL, 0, &instance, &trap);
|
||||||
|
if (instance == NULL)
|
||||||
|
exit_with_error("failed to instantiate", error, trap);
|
||||||
|
|
||||||
|
printf("Creating new `externref`...\n");
|
||||||
|
|
||||||
|
// Create a new `externref` value.
|
||||||
|
wasm_val_t externref;
|
||||||
|
wasmtime_externref_new("Hello, World!", &externref);
|
||||||
|
assert(externref.kind == WASM_ANYREF);
|
||||||
|
|
||||||
|
// The `externref`'s wrapped data should be the string "Hello, World!".
|
||||||
|
void* data = NULL;
|
||||||
|
ok = wasmtime_externref_data(&externref, &data);
|
||||||
|
assert(ok);
|
||||||
|
assert(strcmp((char*)data, "Hello, World!") == 0);
|
||||||
|
|
||||||
|
printf("Touching `externref` table...\n");
|
||||||
|
|
||||||
|
// Lookup the `table` export.
|
||||||
|
wasm_extern_vec_t externs;
|
||||||
|
wasm_instance_exports(instance, &externs);
|
||||||
|
assert(externs.size == 3);
|
||||||
|
wasm_table_t *table = wasm_extern_as_table(externs.data[0]);
|
||||||
|
assert(table != NULL);
|
||||||
|
|
||||||
|
// Set `table[3]` to our `externref`.
|
||||||
|
wasm_val_t elem;
|
||||||
|
wasm_val_copy(&elem, &externref);
|
||||||
|
assert(elem.kind == WASM_ANYREF);
|
||||||
|
ok = wasm_table_set(table, 3, elem.of.ref);
|
||||||
|
assert(ok);
|
||||||
|
|
||||||
|
// `table[3]` should now be our `externref`.
|
||||||
|
wasm_ref_delete(elem.of.ref);
|
||||||
|
elem.of.ref = wasm_table_get(table, 3);
|
||||||
|
assert(elem.of.ref != NULL);
|
||||||
|
assert(wasm_ref_same(elem.of.ref, externref.of.ref));
|
||||||
|
|
||||||
|
printf("Touching `externref` global...\n");
|
||||||
|
|
||||||
|
// Lookup the `global` export.
|
||||||
|
wasm_global_t *global = wasm_extern_as_global(externs.data[1]);
|
||||||
|
assert(global != NULL);
|
||||||
|
|
||||||
|
// Set the global to our `externref`.
|
||||||
|
wasm_global_set(global, &externref);
|
||||||
|
|
||||||
|
// Get the global, and it should return our `externref` again.
|
||||||
|
wasm_val_t global_val;
|
||||||
|
wasm_global_get(global, &global_val);
|
||||||
|
assert(global_val.kind == WASM_ANYREF);
|
||||||
|
assert(wasm_ref_same(global_val.of.ref, externref.of.ref));
|
||||||
|
|
||||||
|
printf("Calling `externref` func...\n");
|
||||||
|
|
||||||
|
// Lookup the `func` export.
|
||||||
|
wasm_func_t *func = wasm_extern_as_func(externs.data[2]);
|
||||||
|
assert(func != NULL);
|
||||||
|
|
||||||
|
// And call it!
|
||||||
|
wasm_val_t args[1];
|
||||||
|
wasm_val_copy(&args[0], &externref);
|
||||||
|
wasm_val_t results[1];
|
||||||
|
error = wasmtime_func_call(func, args, 1, results, 1, &trap);
|
||||||
|
if (error != NULL || trap != NULL)
|
||||||
|
exit_with_error("failed to call function", error, trap);
|
||||||
|
|
||||||
|
// `func` returns the same reference we gave it, so `results[0]` should be our
|
||||||
|
// `externref`.
|
||||||
|
assert(results[0].kind == WASM_ANYREF);
|
||||||
|
assert(wasm_ref_same(results[0].of.ref, externref.of.ref));
|
||||||
|
|
||||||
|
// Clean up after ourselves at this point
|
||||||
|
printf("All finished!\n");
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
wasm_val_delete(&results[0]);
|
||||||
|
wasm_val_delete(&args[0]);
|
||||||
|
wasm_val_delete(&global_val);
|
||||||
|
wasm_val_delete(&elem);
|
||||||
|
wasm_extern_vec_delete(&externs);
|
||||||
|
wasm_val_delete(&externref);
|
||||||
|
wasm_instance_delete(instance);
|
||||||
|
wasm_module_delete(module);
|
||||||
|
wasm_store_delete(store);
|
||||||
|
wasm_engine_delete(engine);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap) {
|
||||||
|
fprintf(stderr, "error: %s\n", message);
|
||||||
|
wasm_byte_vec_t error_message;
|
||||||
|
if (error != NULL) {
|
||||||
|
wasmtime_error_message(error, &error_message);
|
||||||
|
wasmtime_error_delete(error);
|
||||||
|
} else {
|
||||||
|
wasm_trap_message(trap, &error_message);
|
||||||
|
wasm_trap_delete(trap);
|
||||||
|
}
|
||||||
|
fprintf(stderr, "%.*s\n", (int) error_message.size, error_message.data);
|
||||||
|
wasm_byte_vec_delete(&error_message);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
51
examples/externref.rs
Normal file
51
examples/externref.rs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
//! Small example of how to use `externref`s.
|
||||||
|
|
||||||
|
// You can execute this example with `cargo run --example externref`
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use wasmtime::*;
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
println!("Initializing...");
|
||||||
|
let mut config = Config::new();
|
||||||
|
config.wasm_reference_types(true);
|
||||||
|
let engine = Engine::new(&config);
|
||||||
|
let store = Store::new(&engine);
|
||||||
|
|
||||||
|
println!("Compiling module...");
|
||||||
|
let module = Module::from_file(&engine, "examples/externref.wat")?;
|
||||||
|
|
||||||
|
println!("Instantiating module...");
|
||||||
|
let imports = [];
|
||||||
|
let instance = Instance::new(&store, &module, &imports)?;
|
||||||
|
|
||||||
|
println!("Creating new `externref`...");
|
||||||
|
let externref = ExternRef::new("Hello, World!");
|
||||||
|
assert!(externref.data().is::<&'static str>());
|
||||||
|
assert_eq!(
|
||||||
|
*externref.data().downcast_ref::<&'static str>().unwrap(),
|
||||||
|
"Hello, World!"
|
||||||
|
);
|
||||||
|
|
||||||
|
println!("Touching `externref` table...");
|
||||||
|
let table = instance.get_table("table").unwrap();
|
||||||
|
table.set(3, Some(externref.clone()).into())?;
|
||||||
|
let elem = table.get(3).unwrap().unwrap_externref().unwrap();
|
||||||
|
assert!(elem.ptr_eq(&externref));
|
||||||
|
|
||||||
|
println!("Touching `externref` global...");
|
||||||
|
let global = instance.get_global("global").unwrap();
|
||||||
|
global.set(Some(externref.clone()).into())?;
|
||||||
|
let global_val = global.get().unwrap_externref().unwrap();
|
||||||
|
assert!(global_val.ptr_eq(&externref));
|
||||||
|
|
||||||
|
println!("Calling `externref` func...");
|
||||||
|
let func = instance.get_func("func").unwrap();
|
||||||
|
let func = func.get1::<Option<ExternRef>, Option<ExternRef>>()?;
|
||||||
|
let ret = func(Some(externref.clone()))?;
|
||||||
|
assert!(ret.is_some());
|
||||||
|
assert!(ret.unwrap().ptr_eq(&externref));
|
||||||
|
|
||||||
|
println!("Done.");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
9
examples/externref.wat
Normal file
9
examples/externref.wat
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
(module
|
||||||
|
(table $table (export "table") 10 externref)
|
||||||
|
|
||||||
|
(global $global (export "global") (mut externref) (ref.null extern))
|
||||||
|
|
||||||
|
(func (export "func") (param externref) (result externref)
|
||||||
|
local.get 0
|
||||||
|
)
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user