Skip to content

Commit a46300a

Browse files
authored
Add eqref support to the C and C++ APIs (#12914)
* Add `eqref` support to the C and C++ APIs * fix doc build
1 parent 1578325 commit a46300a

11 files changed

Lines changed: 474 additions & 8 deletions

File tree

crates/c-api/include/wasmtime.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@
205205
#include <wasmtime/tag.h>
206206
#include <wasmtime/trap.h>
207207
#include <wasmtime/val.h>
208+
#include <wasmtime/gc.h>
208209
#include <wasmtime/async.h>
209210
#include <wasmtime/component.h>
210211
#include <wasmtime/wat.h>

crates/c-api/include/wasmtime.hh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include <wasmtime/exn.hh>
4242
#include <wasmtime/extern.hh>
4343
#include <wasmtime/func.hh>
44+
#include <wasmtime/gc.hh>
4445
#include <wasmtime/global.hh>
4546
#include <wasmtime/instance.hh>
4647
#include <wasmtime/linker.hh>

crates/c-api/include/wasmtime/gc.h

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/**
2+
* \file wasmtime/gc.h
3+
*
4+
* APIs for interacting with WebAssembly GC types in Wasmtime.
5+
*
6+
* This header provides types and functions for GC reference types beyond
7+
* the basic `anyref` and `externref` in val.h: `eqref`, `structref`,
8+
* and `arrayref`.
9+
*/
10+
11+
#ifndef WASMTIME_GC_H
12+
#define WASMTIME_GC_H
13+
14+
#include <wasmtime/val.h>
15+
16+
#ifdef __cplusplus
17+
extern "C" {
18+
#endif
19+
20+
/**
21+
* \typedef wasmtime_eqref_t
22+
* \brief Convenience alias for #wasmtime_eqref
23+
*
24+
* \struct wasmtime_eqref
25+
* \brief A WebAssembly `eqref` value.
26+
*
27+
* This structure represents a reference to a GC object that can be tested for
28+
* equality. The subtypes of `eqref` include `structref`, `arrayref`, and
29+
* `i31ref`.
30+
*
31+
* This type has the same representation and ownership semantics as
32+
* #wasmtime_anyref_t. Values must be explicitly unrooted via
33+
* #wasmtime_eqref_unroot to enable garbage collection.
34+
*/
35+
typedef struct wasmtime_eqref {
36+
/// Internal metadata tracking within the store, embedders should not
37+
/// configure or modify these fields.
38+
uint64_t store_id;
39+
/// Internal to Wasmtime.
40+
uint32_t __private1;
41+
/// Internal to Wasmtime.
42+
uint32_t __private2;
43+
/// Internal to Wasmtime.
44+
void *__private3;
45+
} wasmtime_eqref_t;
46+
47+
/// \brief Initialize the `ref` to a null `eqref` value.
48+
static inline void wasmtime_eqref_set_null(wasmtime_eqref_t *ref) {
49+
ref->store_id = 0;
50+
}
51+
52+
/// \brief Returns whether the provided `ref` is a null `eqref` value.
53+
static inline bool wasmtime_eqref_is_null(const wasmtime_eqref_t *ref) {
54+
return ref->store_id == 0;
55+
}
56+
57+
/**
58+
* \brief Clone an `eqref`, creating a new root.
59+
*
60+
* The cloned reference is stored in `out`.
61+
*/
62+
WASM_API_EXTERN void wasmtime_eqref_clone(const wasmtime_eqref_t *eqref,
63+
wasmtime_eqref_t *out);
64+
65+
/**
66+
* \brief Unroot an `eqref` to allow garbage collection.
67+
*
68+
* After calling this, `ref` is left in an undefined state and should not be
69+
* used again.
70+
*/
71+
WASM_API_EXTERN void wasmtime_eqref_unroot(wasmtime_eqref_t *ref);
72+
73+
/**
74+
* \brief Upcast an `eqref` to an `anyref`.
75+
*
76+
* The original `eqref` is not consumed; `out` receives a new cloned root
77+
* pointing to the same GC object as `anyref`.
78+
*/
79+
WASM_API_EXTERN void wasmtime_eqref_to_anyref(const wasmtime_eqref_t *eqref,
80+
wasmtime_anyref_t *out);
81+
82+
/**
83+
* \brief Create a new `i31ref` value.
84+
*
85+
* Creates a new `i31ref` value (which is a subtype of `eqref`) and returns a
86+
* pointer to it.
87+
*
88+
* If `i31val` does not fit in 31 bits, it is wrapped.
89+
*/
90+
WASM_API_EXTERN void wasmtime_eqref_from_i31(wasmtime_context_t *context,
91+
uint32_t i31val,
92+
wasmtime_eqref_t *out);
93+
94+
/**
95+
* \brief Test whether this `eqref` is an `i31ref`.
96+
*
97+
* Returns `true` if the given `eqref` is an `i31ref`, `false` otherwise.
98+
* Returns `false` for null references.
99+
*/
100+
WASM_API_EXTERN bool wasmtime_eqref_is_i31(wasmtime_context_t *context,
101+
const wasmtime_eqref_t *eqref);
102+
103+
/**
104+
* \brief Get the `eqref`'s underlying `i31ref` value, zero extended.
105+
*
106+
* If the given `eqref` is an instance of `i31ref`, then its value is zero
107+
* extended to 32 bits, written to `dst`, and `true` is returned.
108+
*
109+
* If the given `eqref` is not an instance of `i31ref`, then `false` is
110+
* returned and `dst` is left unmodified.
111+
*/
112+
WASM_API_EXTERN bool wasmtime_eqref_i31_get_u(wasmtime_context_t *context,
113+
const wasmtime_eqref_t *eqref,
114+
uint32_t *dst);
115+
116+
/**
117+
* \brief Get the `eqref`'s underlying `i31ref` value, sign extended.
118+
*
119+
* If the given `eqref` is an instance of `i31ref`, then its value is sign
120+
* extended to 32 bits, written to `dst`, and `true` is returned.
121+
*
122+
* If the given `eqref` is not an instance of `i31ref`, then `false` is
123+
* returned and `dst` is left unmodified.
124+
*/
125+
WASM_API_EXTERN bool wasmtime_eqref_i31_get_s(wasmtime_context_t *context,
126+
const wasmtime_eqref_t *eqref,
127+
int32_t *dst);
128+
129+
#ifdef __cplusplus
130+
} // extern "C"
131+
#endif
132+
133+
#endif // WASMTIME_GC_H
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/**
2+
* \file wasmtime/gc.hh
3+
*
4+
* C++ API for WebAssembly GC types: eqref, structref, and arrayref.
5+
*/
6+
7+
#ifndef WASMTIME_GC_HH
8+
#define WASMTIME_GC_HH
9+
10+
#include <wasmtime/gc.h>
11+
#include <wasmtime/val.hh>
12+
13+
namespace wasmtime {
14+
15+
/**
16+
* \brief Representation of a WebAssembly `eqref` value.
17+
*
18+
* An `eqref` is a reference to a GC object that supports equality testing.
19+
* Subtypes include `structref`, `arrayref`, and `i31ref`.
20+
*
21+
* Like all GC references, `EqRef` values are rooted in a `Store` and must be
22+
* unrooted (by destruction or move) to allow garbage collection.
23+
*/
24+
class EqRef {
25+
friend class Val;
26+
friend class AnyRef;
27+
28+
wasmtime_eqref_t val;
29+
30+
public:
31+
/// Creates a new `EqRef` from its C-API representation.
32+
explicit EqRef(wasmtime_eqref_t val) : val(val) {}
33+
34+
/// Copy constructor.
35+
EqRef(const EqRef &other) { wasmtime_eqref_clone(&other.val, &val); }
36+
37+
/// Copy assignment.
38+
EqRef &operator=(const EqRef &other) {
39+
wasmtime_eqref_unroot(&val);
40+
wasmtime_eqref_clone(&other.val, &val);
41+
return *this;
42+
}
43+
44+
/// Move constructor.
45+
EqRef(EqRef &&other) {
46+
val = other.val;
47+
wasmtime_eqref_set_null(&other.val);
48+
}
49+
50+
/// Move assignment.
51+
EqRef &operator=(EqRef &&other) {
52+
wasmtime_eqref_unroot(&val);
53+
val = other.val;
54+
wasmtime_eqref_set_null(&other.val);
55+
return *this;
56+
}
57+
58+
~EqRef() { wasmtime_eqref_unroot(&val); }
59+
60+
/// Create an `eqref` from an i31 value.
61+
static EqRef from_i31(Store::Context cx, uint32_t val) {
62+
wasmtime_eqref_t out;
63+
wasmtime_eqref_from_i31(cx.capi(), val, &out);
64+
return EqRef(out);
65+
}
66+
67+
/// Returns `true` if this eqref is an i31ref.
68+
bool is_i31(Store::Context cx) const {
69+
return wasmtime_eqref_is_i31(cx.capi(), &val);
70+
}
71+
72+
/// Get the i31 value as an unsigned 32-bit integer.
73+
/// Returns `std::nullopt` if this eqref is not an i31ref.
74+
std::optional<uint32_t> i31_get_u(Store::Context cx) const {
75+
uint32_t dst;
76+
if (wasmtime_eqref_i31_get_u(cx.capi(), &val, &dst))
77+
return dst;
78+
return std::nullopt;
79+
}
80+
81+
/// Get the i31 value as a signed 32-bit integer.
82+
/// Returns `std::nullopt` if this eqref is not an i31ref.
83+
std::optional<int32_t> i31_get_s(Store::Context cx) const {
84+
int32_t dst;
85+
if (wasmtime_eqref_i31_get_s(cx.capi(), &val, &dst))
86+
return dst;
87+
return std::nullopt;
88+
}
89+
90+
/// Upcast this `eqref` to an `anyref`.
91+
AnyRef to_anyref() const {
92+
wasmtime_anyref_t out;
93+
wasmtime_eqref_to_anyref(&val, &out);
94+
return AnyRef(out);
95+
}
96+
};
97+
98+
} // namespace wasmtime
99+
100+
#endif // WASMTIME_GC_HH

crates/c-api/include/wasmtime/store.hh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ public:
7575
friend class Linker;
7676
friend class ExternRef;
7777
friend class AnyRef;
78+
friend class EqRef;
7879
friend class Val;
7980
friend class Store;
8081
friend class Tag;

crates/c-api/include/wasmtime/val.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
extern "C" {
1616
#endif
1717

18+
struct wasmtime_eqref;
19+
/// Convenience alias for #wasmtime_eqref
20+
typedef struct wasmtime_eqref wasmtime_eqref_t;
21+
1822
/**
1923
* \typedef wasmtime_anyref_t
2024
* \brief Convenience alias for #wasmtime_anyref
@@ -77,6 +81,19 @@ static inline bool wasmtime_anyref_is_null(const wasmtime_anyref_t *ref) {
7781
WASM_API_EXTERN void wasmtime_anyref_clone(const wasmtime_anyref_t *anyref,
7882
wasmtime_anyref_t *out);
7983

84+
/**
85+
* \brief Downcast an `anyref` to an `eqyref`.
86+
*
87+
* Returns `true` if the downcast succeeded, and `out` is initialized. Returns
88+
* `false` if the downcast failed, and `out` is left uninitialized.
89+
*
90+
* The original `anyref` is not consumed; `out` receives a new cloned root
91+
* pointing to the same GC object as `anyref`.
92+
*/
93+
WASM_API_EXTERN bool wasmtime_anyref_to_eqref(wasmtime_context_t *context,
94+
const wasmtime_anyref_t *anyref,
95+
wasmtime_eqref_t *out);
96+
8097
/**
8198
* \brief Unroots the `ref` provided within the `context`.
8299
*
@@ -128,6 +145,15 @@ WASM_API_EXTERN void wasmtime_anyref_from_i31(wasmtime_context_t *context,
128145
uint32_t i31val,
129146
wasmtime_anyref_t *out);
130147

148+
/**
149+
* \brief Test whether an `anyref` is an `i31ref`.
150+
*
151+
* Returns `true` if the given `anyref` is an `i31ref`, `false` otherwise.
152+
* Returns `false` for null references.
153+
*/
154+
WASM_API_EXTERN bool wasmtime_anyref_is_i31(wasmtime_context_t *context,
155+
const wasmtime_anyref_t *anyref);
156+
131157
/**
132158
* \brief Get the `anyref`'s underlying `i31ref` value, zero extended, if any.
133159
*

crates/c-api/include/wasmtime/val.hh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#define WASMTIME_VAL_HH
77

88
#include <optional>
9+
#include <wasmtime/gc.h>
910
#include <wasmtime/store.hh>
1011
#include <wasmtime/types/val.hh>
1112
#include <wasmtime/val.h>
@@ -99,6 +100,8 @@ public:
99100
}
100101
};
101102

103+
class EqRef;
104+
102105
/**
103106
* \brief Representation of a WebAssembly `anyref` value.
104107
*/
@@ -173,6 +176,11 @@ public:
173176
return ret;
174177
return std::nullopt;
175178
}
179+
180+
/// \brief Returns `true` if this anyref is an i31ref.
181+
bool is_i31(Store::Context cx) const {
182+
return wasmtime_anyref_is_i31(cx.ptr, &val);
183+
}
176184
};
177185

178186
/// \brief Container for the `v128` WebAssembly type.

0 commit comments

Comments
 (0)