Skip to content

Commit

Permalink
wasmtime-c-api: Add support for GC references in wasmtime.h APIs
Browse files Browse the repository at this point in the history
Restores support for `externref` in `wasmtime_val_t`, methods for manipulating
them and getting their wrapped host data, and examples/tests for these things.

Additionally adds support for `anyref` in `wasmtime_val_t`, clone/delete methods
similar to those for `externref`, and a few `i31ref`-specific methods. Also adds
C and Rust example / test for working with `anyref`.
  • Loading branch information
fitzgen committed Apr 12, 2024
1 parent e79664b commit e63c137
Show file tree
Hide file tree
Showing 12 changed files with 975 additions and 34 deletions.
15 changes: 15 additions & 0 deletions crates/c-api/include/wasmtime/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,21 @@ WASMTIME_CONFIG_PROP(void, wasm_tail_call, bool)
*/
WASMTIME_CONFIG_PROP(void, wasm_reference_types, bool)

/**
* \brief Configures whether the WebAssembly typed function reference types
* proposal is enabled.
*
* This setting is `false` by default.
*/
WASMTIME_CONFIG_PROP(void, wasm_function_references, bool)

/**
* \brief Configures whether the WebAssembly GC proposal is enabled.
*
* This setting is `false` by default.
*/
WASMTIME_CONFIG_PROP(void, wasm_gc, bool)

/**
* \brief Configures whether the WebAssembly SIMD proposal is
* enabled.
Expand Down
208 changes: 205 additions & 3 deletions crates/c-api/include/wasmtime/val.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,174 @@
extern "C" {
#endif

/**
* \typedef wasmtime_anyref_t
* \brief Convenience alias for #wasmtime_anyref
*
* \struct wasmtime_anyref
* \brief A host-defined un-forgeable reference to pass into WebAssembly.
*
* This structure represents an `anyref` that can be passed to WebAssembly.
* It cannot be forged by WebAssembly itself and is guaranteed to have been
* created by the host.
*/
typedef struct wasmtime_anyref wasmtime_anyref_t;

/**
* \brief Creates a shallow copy of the `anyref` argument, returning a
* separately owned pointer (depending on the configured collector this might
* increase a reference count or create a new GC root).
*/
WASM_API_EXTERN wasmtime_anyref_t *
wasmtime_anyref_clone(wasmtime_context_t *context, wasmtime_anyref_t *ref);

/**
* \brief Drops an owned pointer to `ref`, potentially deleting it if it's the
* last reference, or allowing it to be collected during the next GC.
*/
WASM_API_EXTERN void wasmtime_anyref_delete(wasmtime_context_t *context,
wasmtime_anyref_t *ref);

/**
* \brief Converts a raw `anyref` value coming from #wasmtime_val_raw_t into
* a #wasmtime_anyref_t.
*
* Note that the returned #wasmtime_anyref_t is an owned value that must be
* deleted via #wasmtime_anyref_delete by the caller if it is non-null.
*/
WASM_API_EXTERN wasmtime_anyref_t *
wasmtime_anyref_from_raw(wasmtime_context_t *context, uint32_t raw);

/**
* \brief Converts a #wasmtime_anyref_t to a raw value suitable for storing
* into a #wasmtime_val_raw_t.
*
* Note that the returned underlying value is not tracked by Wasmtime's garbage
* collector until it enters WebAssembly. This means that a GC may release the
* context's reference to the raw value, making the raw value invalid within the
* context of the store. Do not perform a GC between calling this function and
* passing it to WebAssembly.
*/
WASM_API_EXTERN uint32_t wasmtime_anyref_to_raw(wasmtime_context_t *context,
const wasmtime_anyref_t *ref);

/**
* \brief Create a new `i31ref` value.
*
* Creates a new `i31ref` value (which is a subtype of `anyref`) and returns a
* pointer to it.
*
* If `i31val` does not fit in 31 bits, it is wrapped.
*/
WASM_API_EXTERN wasmtime_anyref_t *
wasmtime_anyref_from_i31(wasmtime_context_t *context, uint32_t i31val);

/**
* \brief Get the `anyref`'s underlying `i31ref` value, zero extended, if any.
*
* If the given `anyref` is an instance of `i31ref`, then its value is zero
* extended to 32 bits, written to `dst`, and `true` is returned.
*
* If the given `anyref` is not an instance of `i31ref`, then `false` is
* returned and `dst` is left unmodified.
*/
WASM_API_EXTERN bool wasmtime_anyref_i31_get_u(wasmtime_context_t *context,
wasmtime_anyref_t *anyref,
uint32_t *dst);

/**
* \brief Get the `anyref`'s underlying `i31ref` value, sign extended, if any.
*
* If the given `anyref` is an instance of `i31ref`, then its value is sign
* extended to 32 bits, written to `dst`, and `true` is returned.
*
* If the given `anyref` is not an instance of `i31ref`, then `false` is
* returned and `dst` is left unmodified.
*/
WASM_API_EXTERN bool wasmtime_anyref_i31_get_s(wasmtime_context_t *context,
wasmtime_anyref_t *anyref,
int32_t *dst);

/**
* \typedef wasmtime_externref_t
* \brief Convenience alias for #wasmtime_externref
*
* \struct wasmtime_externref
* \brief A host-defined un-forgeable reference to pass into WebAssembly.
*
* This structure represents an `externref` that can be passed to WebAssembly.
* It cannot be forged by WebAssembly itself and is guaranteed to have been
* created by the host.
*/
typedef struct wasmtime_externref wasmtime_externref_t;

/**
* \brief Create a new `externref` value.
*
* Creates a new `externref` value wrapping the provided data, returning the
* pointer to the externref.
*
* \param data the host-specific data to wrap
* \param finalizer an optional finalizer for `data`
*
* When the reference is reclaimed, the wrapped data is cleaned up with the
* provided `finalizer`.
*
* The returned value must be deleted with #wasmtime_externref_delete and may
* not be used after the context is destroyed.
*/
WASM_API_EXTERN wasmtime_externref_t *
wasmtime_externref_new(wasmtime_context_t *context, void *data,
void (*finalizer)(void *));

/**
* \brief Get an `externref`'s wrapped data
*
* Returns the original `data` passed to #wasmtime_externref_new. It is required
* that `data` is not `NULL`.
*/
WASM_API_EXTERN void *wasmtime_externref_data(wasmtime_context_t *context,
wasmtime_externref_t *data);

/**
* \brief Creates a shallow copy of the `externref` argument, returning a
* separately owned pointer (depending on the configured collector this might
* increase a reference count or create a new GC root).
*/
WASM_API_EXTERN wasmtime_externref_t *
wasmtime_externref_clone(wasmtime_context_t *context,
wasmtime_externref_t *ref);

/**
* \brief Drops an owned pointer to `ref`, potentially deleting it if it's the
* last reference, or allowing it to be collected during the next GC.
*/
WASM_API_EXTERN void wasmtime_externref_delete(wasmtime_context_t *context,
wasmtime_externref_t *ref);

/**
* \brief Converts a raw `externref` value coming from #wasmtime_val_raw_t into
* a #wasmtime_externref_t.
*
* Note that the returned #wasmtime_externref_t is an owned value that must be
* deleted via #wasmtime_externref_delete by the caller if it is non-null.
*/
WASM_API_EXTERN wasmtime_externref_t *
wasmtime_externref_from_raw(wasmtime_context_t *context, uint32_t raw);

/**
* \brief Converts a #wasmtime_externref_t to a raw value suitable for storing
* into a #wasmtime_val_raw_t.
*
* Note that the returned underlying value is not tracked by Wasmtime's garbage
* collector until it enters WebAssembly. This means that a GC may release the
* context's reference to the raw value, making the raw value invalid within the
* context of the store. Do not perform a GC between calling this function and
* passing it to WebAssembly.
*/
WASM_API_EXTERN uint32_t wasmtime_externref_to_raw(
wasmtime_context_t *context, const wasmtime_externref_t *ref);

/// \brief Discriminant stored in #wasmtime_val::kind
typedef uint8_t wasmtime_valkind_t;
/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is an i32
Expand All @@ -29,6 +197,12 @@ typedef uint8_t wasmtime_valkind_t;
/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is a
/// funcref
#define WASMTIME_FUNCREF 5
/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is an
/// externref
#define WASMTIME_EXTERNREF 6
/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is an
/// anyref
#define WASMTIME_ANYREF 7

/// \brief A 128-bit value representing the WebAssembly `v128` type. Bytes are
/// stored in little-endian order.
Expand All @@ -53,6 +227,16 @@ typedef union wasmtime_valunion {
float32_t f32;
/// Field used if #wasmtime_val_t::kind is #WASMTIME_F64
float64_t f64;
/// Field used if #wasmtime_val_t::kind is #WASMTIME_ANYREF
///
/// If this value represents a `ref.null any` value then this pointer will
/// be `NULL`.
wasmtime_anyref_t *anyref;
/// Field used if #wasmtime_val_t::kind is #WASMTIME_EXTERNREF
///
/// If this value represents a `ref.null extern` value then this pointer will
/// be `NULL`.
wasmtime_externref_t *externref;
/// Field used if #wasmtime_val_t::kind is #WASMTIME_FUNCREF
///
/// If this value represents a `ref.null func` value then the `store_id` field
Expand Down Expand Up @@ -96,6 +280,22 @@ typedef union wasmtime_val_raw {
///
/// Note that this field is always stored in a little-endian format.
wasmtime_v128 v128;
/// Field for when this val is a WebAssembly `anyref` value.
///
/// If this is set to 0 then it's a null anyref, otherwise this must be
/// passed to `wasmtime_anyref_from_raw` to determine the
/// `wasmtime_anyref_t`.
///
/// Note that this field is always stored in a little-endian format.
uint32_t anyref;
/// Field for when this val is a WebAssembly `externref` value.
///
/// If this is set to 0 then it's a null externref, otherwise this must be
/// passed to `wasmtime_externref_from_raw` to determine the
/// `wasmtime_externref_t`.
///
/// Note that this field is always stored in a little-endian format.
uint32_t externref;
/// Field for when this val is a WebAssembly `funcref` value.
///
/// If this is set to 0 then it's a null funcref, otherwise this must be
Expand All @@ -112,9 +312,11 @@ typedef union wasmtime_val_raw {
* \union wasmtime_val
* \brief Container for different kinds of wasm values.
*
* APIs which consume a #wasmtime_val_t do not take ownership, but APIs that
* return #wasmtime_val_t require that #wasmtime_val_delete is called to
* deallocate the value.
* Note that this structure may contain an owned value, namely rooted GC
* references, depending on the context in which this is used. APIs which
* consume a #wasmtime_val_t do not take ownership, but APIs that return
* #wasmtime_val_t require that #wasmtime_val_delete is called to deallocate the
* value.
*/
typedef struct wasmtime_val {
/// Discriminant of which field of #of is valid.
Expand Down
13 changes: 13 additions & 0 deletions crates/c-api/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,19 @@ pub extern "C" fn wasmtime_config_wasm_reference_types_set(c: &mut wasm_config_t
c.config.wasm_reference_types(enable);
}

#[no_mangle]
pub extern "C" fn wasmtime_config_wasm_function_references_set(
c: &mut wasm_config_t,
enable: bool,
) {
c.config.wasm_function_references(enable);
}

#[no_mangle]
pub extern "C" fn wasmtime_config_wasm_gc_set(c: &mut wasm_config_t, enable: bool) {
c.config.wasm_gc(enable);
}

#[no_mangle]
pub extern "C" fn wasmtime_config_wasm_simd_set(c: &mut wasm_config_t, enable: bool) {
c.config.wasm_simd(enable);
Expand Down
Loading

0 comments on commit e63c137

Please sign in to comment.