Skip to content

Commit 9bafb17

Browse files
authored
Merge pull request #1996 from fitzgen/ref-types-in-c-api
Support reference types in the C API
2 parents 4b6ebc0 + 3638dba commit 9bafb17

File tree

17 files changed

+591
-290
lines changed

17 files changed

+591
-290
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,8 @@ jobs:
231231

232232
# Ensure all our examples build and execute
233233
- run: cargo run -p run-examples
234+
env:
235+
RUST_BACKTRACE: 1
234236

235237
# Build and test all features except for lightbeam
236238
- run: |

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ rusty-tags.*
1515
tags
1616
target
1717
.z3-trace
18+
foo

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/c-api/include/doc-wasm.h

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,19 +1147,22 @@
11471147

11481148
/**
11491149
* \struct wasm_ref_t
1150-
* \brief Unimplemented and used in Wasmtime right now.
1150+
* \brief A reference type: either a funcref or an externref.
11511151
*
11521152
* \typedef wasm_ref_t
11531153
* \brief Convenience alias for #wasm_ref_t
11541154
*
11551155
* \fn void wasm_ref_delete(own wasm_ref_t *v);
1156-
* \brief Deletes a reference.
1156+
* \brief Delete a reference.
11571157
*
11581158
* \fn own wasm_ref_t *wasm_ref_copy(const wasm_ref_t *)
1159-
* \brief Unimplemented in Wasmtime, aborts the process if called.
1159+
* \brief Copy a reference.
11601160
*
11611161
* \fn bool wasm_ref_same(const wasm_ref_t *, const wasm_ref_t *)
1162-
* \brief Unimplemented in Wasmtime, aborts the process if called.
1162+
* \brief Are the given references pointing to the same externref?
1163+
*
1164+
* > Note: Wasmtime does not support checking funcrefs for equality, and this
1165+
* > function will always return false for funcrefs.
11631166
*
11641167
* \fn void* wasm_ref_get_host_info(const wasm_ref_t *);
11651168
* \brief Unimplemented in Wasmtime, always returns `NULL`.
@@ -1614,6 +1617,9 @@
16141617
* If a trap happens during execution or some other error then a non-`NULL` trap
16151618
* is returned. In this situation the `results` are is unmodified.
16161619
*
1620+
* Does not take ownership of `wasm_val_t` arguments. Gives ownership of
1621+
* `wasm_val_t` results.
1622+
*
16171623
* > Note: to avoid the UB associated with passing the wrong number of results
16181624
* > or parameters by accident, or to distinguish between traps and other
16191625
* > errors, it's recommended to use #wasmtime_func_call.
@@ -1758,10 +1764,9 @@
17581764
* Returns an error if the #wasm_ref_t does not match the element type of the
17591765
* table provided or if it comes from a different store than the one provided.
17601766
*
1767+
* Does not take ownship of the `init` value.
1768+
*
17611769
* > Note: for funcref tables you can use #wasmtime_funcref_table_new as well.
1762-
* >
1763-
* > Additionally the #wasm_ref_t does not have much support in Wasmtime, so you
1764-
* > may not be able to create an appropriate initial value.
17651770
*
17661771
* \fn wasm_tabletype_t *wasm_table_type(const wasm_table_t *);
17671772
* \brief Returns the type of this table.
@@ -1774,12 +1779,10 @@
17741779
* Attempts to get a value at an index in this table. This function returns
17751780
* `NULL` if the index is out of bounds.
17761781
*
1782+
* Gives ownership of the resulting `wasm_ref_t*`.
1783+
*
17771784
* > Note: for funcref tables you can use #wasmtime_funcref_table_get to learn
17781785
* > about out-of-bounds errors.
1779-
* >
1780-
* > Additionally the #wasm_ref_t does not have much
1781-
* > support in Wasmtime, so you may not be able to do much with the returned
1782-
* > value.
17831786
*
17841787
* \fn void wasm_table_set(wasm_table_t *, wasm_table_size_t index, wasm_ref_t *);
17851788
* \brief Sets an element in this table.
@@ -1791,11 +1794,10 @@
17911794
* * The #wasm_ref_t comes from a different store than the table provided.
17921795
* * The #wasm_ref_t does not have an appropriate type to store in this table.
17931796
*
1797+
* Does not take ownership of the given `wasm_ref_t*`.
1798+
*
17941799
* > Note: for funcref tables you can use #wasmtime_funcref_table_set to learn
17951800
* > about errors.
1796-
* >
1797-
* > Additionally the #wasm_ref_t does not have much support in Wasmtime, so you
1798-
* > may not be able to create an appropriate initial value.
17991801
*
18001802
* \fn wasm_table_size_t wasm_table_size(const wasm_table_t *);
18011803
* \brief Gets the current size, in elements, of this table.
@@ -1813,10 +1815,9 @@
18131815
* * The #wasm_ref_t comes from a different store than the table provided.
18141816
* * The #wasm_ref_t does not have an appropriate type to store in this table.
18151817
*
1818+
* Does not take ownership of the givein `init` value.
1819+
*
18161820
* > Note: for funcref tables you can use #wasmtime_funcref_table_grow as well.
1817-
* >
1818-
* > Additionally the #wasm_ref_t does not have much support in Wasmtime, so you
1819-
* > may not be able to create an appropriate initial value.
18201821
*/
18211822

18221823
/**

crates/c-api/include/wasmtime.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,9 @@ WASM_API_EXTERN const wasm_name_t *wasmtime_frame_module_name(const wasm_frame_t
651651
*
652652
* The `trap` pointer cannot be `NULL`. The `args` and `results` pointers may be
653653
* `NULL` if the corresponding length is zero.
654+
*
655+
* Does not take ownership of `wasm_val_t` arguments. Gives ownership of
656+
* `wasm_val_t` results.
654657
*/
655658
WASM_API_EXTERN own wasmtime_error_t *wasmtime_func_call(
656659
wasm_func_t *func,
@@ -833,6 +836,63 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_funcref_table_grow(
833836
wasm_table_size_t *prev_size
834837
);
835838

839+
/**
840+
* \brief Create a new `externref` value.
841+
*
842+
* Creates a new `externref` value wrapping the provided data, and writes it to
843+
* `valp`.
844+
*
845+
* This function does not take an associated finalizer to clean up the data when
846+
* the reference is reclaimed. If you need a finalizer to clean up the data,
847+
* then use #wasmtime_externref_new_with_finalizer.
848+
*/
849+
WASM_API_EXTERN void wasmtime_externref_new(void *data, wasm_val_t *valp);
850+
851+
/**
852+
* \brief A finalizer for an `externref`'s wrapped data.
853+
*
854+
* A finalizer callback to clean up an `externref`'s wrapped data after the
855+
* `externref` has been reclaimed. This is an opportunity to run destructors,
856+
* free dynamically allocated memory, close file handles, etc.
857+
*/
858+
typedef void (*wasmtime_externref_finalizer_t)(void*);
859+
860+
/**
861+
* \brief Create a new `externref` value with a finalizer.
862+
*
863+
* Creates a new `externref` value wrapping the provided data, and writes it to
864+
* `valp`.
865+
*
866+
* When the reference is reclaimed, the wrapped data is cleaned up with the
867+
* provided finalizer. If you do not need to clean up the wrapped data, then use
868+
* #wasmtime_externref_new.
869+
*/
870+
WASM_API_EXTERN void wasmtime_externref_new_with_finalizer(
871+
void *data,
872+
wasmtime_externref_finalizer_t finalizer,
873+
wasm_val_t *valp
874+
);
875+
876+
/**
877+
* \brief Get an `externref`'s wrapped data
878+
*
879+
* If the given value is a reference to a non-null `externref`, writes the
880+
* wrapped data that was passed into #wasmtime_externref_new or
881+
* #wasmtime_externref_new_with_finalizer when creating the given `externref` to
882+
* `datap`, and returns `true`.
883+
*
884+
* If the value is a reference to a null `externref`, writes `NULL` to `datap`
885+
* and returns `true`.
886+
*
887+
* If the given value is not an `externref`, returns `false` and leaves `datap`
888+
* unmodified.
889+
*
890+
* Does not take ownership of `val`.
891+
*
892+
* Both `val` and `datap` must not be `NULL`.
893+
*/
894+
WASM_API_EXTERN bool wasmtime_externref_data(wasm_val_t* val, void** datap);
895+
836896
#undef own
837897

838898
#ifdef __cplusplus

crates/c-api/src/func.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::{wasm_extern_t, wasm_functype_t, wasm_store_t, wasm_val_t};
22
use crate::{wasm_name_t, wasm_trap_t, wasmtime_error_t};
33
use anyhow::anyhow;
44
use std::ffi::c_void;
5+
use std::mem::MaybeUninit;
56
use std::panic::{self, AssertUnwindSafe};
67
use std::ptr;
78
use std::str;
@@ -89,6 +90,7 @@ fn create_function(
8990
let func = Func::new(store, ty, move |caller, params, results| {
9091
let params = params
9192
.iter()
93+
.cloned()
9294
.map(|p| wasm_val_t::from_val(p))
9395
.collect::<Vec<_>>();
9496
let mut out_results = vec![wasm_val_t::default(); results.len()];
@@ -163,7 +165,7 @@ pub extern "C" fn wasmtime_func_new_with_env(
163165
pub unsafe extern "C" fn wasm_func_call(
164166
wasm_func: &wasm_func_t,
165167
args: *const wasm_val_t,
166-
results: *mut wasm_val_t,
168+
results: *mut MaybeUninit<wasm_val_t>,
167169
) -> *mut wasm_trap_t {
168170
let func = wasm_func.func();
169171
let mut trap = ptr::null_mut();
@@ -186,7 +188,7 @@ pub unsafe extern "C" fn wasmtime_func_call(
186188
func: &wasm_func_t,
187189
args: *const wasm_val_t,
188190
num_args: usize,
189-
results: *mut wasm_val_t,
191+
results: *mut MaybeUninit<wasm_val_t>,
190192
num_results: usize,
191193
trap_ptr: &mut *mut wasm_trap_t,
192194
) -> Option<Box<wasmtime_error_t>> {
@@ -201,7 +203,7 @@ pub unsafe extern "C" fn wasmtime_func_call(
201203
fn _wasmtime_func_call(
202204
func: &wasm_func_t,
203205
args: &[wasm_val_t],
204-
results: &mut [wasm_val_t],
206+
results: &mut [MaybeUninit<wasm_val_t>],
205207
trap_ptr: &mut *mut wasm_trap_t,
206208
) -> Option<Box<wasmtime_error_t>> {
207209
let func = func.func();
@@ -217,8 +219,8 @@ fn _wasmtime_func_call(
217219
let result = panic::catch_unwind(AssertUnwindSafe(|| func.call(&params)));
218220
match result {
219221
Ok(Ok(out)) => {
220-
for (slot, val) in results.iter_mut().zip(out.iter()) {
221-
*slot = wasm_val_t::from_val(val);
222+
for (slot, val) in results.iter_mut().zip(out.into_vec().into_iter()) {
223+
crate::initialize(slot, wasm_val_t::from_val(val));
222224
}
223225
None
224226
}

crates/c-api/src/global.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::{handle_result, wasmtime_error_t};
22
use crate::{wasm_extern_t, wasm_globaltype_t, wasm_store_t, wasm_val_t};
3+
use std::mem::MaybeUninit;
34
use std::ptr;
45
use wasmtime::{Extern, Global};
56

@@ -72,8 +73,8 @@ pub extern "C" fn wasm_global_type(g: &wasm_global_t) -> Box<wasm_globaltype_t>
7273
}
7374

7475
#[no_mangle]
75-
pub extern "C" fn wasm_global_get(g: &wasm_global_t, out: &mut wasm_val_t) {
76-
out.set(g.global().get());
76+
pub extern "C" fn wasm_global_get(g: &wasm_global_t, out: &mut MaybeUninit<wasm_val_t>) {
77+
crate::initialize(out, wasm_val_t::from_val(g.global().get()));
7778
}
7879

7980
#[no_mangle]

crates/c-api/src/lib.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,13 @@ pub struct wasm_shared_module_t {
6363
_unused: [u8; 0],
6464
}
6565

66-
struct HostInfoState {
67-
info: *mut std::ffi::c_void,
68-
finalizer: Option<extern "C" fn(arg1: *mut std::ffi::c_void)>,
69-
}
70-
71-
impl Drop for HostInfoState {
72-
fn drop(&mut self) {
73-
if let Some(f) = &self.finalizer {
74-
f(self.info);
75-
}
66+
/// Initialize a `MaybeUninit<T>`
67+
///
68+
/// TODO: Replace calls to this function with
69+
/// https://doc.rust-lang.org/nightly/std/mem/union.MaybeUninit.html#method.write
70+
/// once it is stable.
71+
pub(crate) fn initialize<T>(dst: &mut std::mem::MaybeUninit<T>, val: T) {
72+
unsafe {
73+
std::ptr::write(dst.as_mut_ptr(), val);
7674
}
7775
}

0 commit comments

Comments
 (0)