Skip to content

Commit 4d7bb3b

Browse files
committed
Add eqref support to the C and C++ APIs
1 parent d2dee5d commit 4d7bb3b

File tree

11 files changed

+475
-8
lines changed

11 files changed

+475
-8
lines changed

crates/c-api/include/wasmtime.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@
203203
#include <wasmtime/table.h>
204204
#include <wasmtime/trap.h>
205205
#include <wasmtime/val.h>
206+
#include <wasmtime/gc.h>
206207
#include <wasmtime/async.h>
207208
#include <wasmtime/component.h>
208209
#include <wasmtime/wat.h>

crates/c-api/include/wasmtime.hh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include <wasmtime/error.hh>
4141
#include <wasmtime/extern.hh>
4242
#include <wasmtime/func.hh>
43+
#include <wasmtime/gc.hh>
4344
#include <wasmtime/global.hh>
4445
#include <wasmtime/instance.hh>
4546
#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
@@ -72,6 +72,7 @@ public:
7272
friend class Linker;
7373
friend class ExternRef;
7474
friend class AnyRef;
75+
friend class EqRef;
7576
friend class Val;
7677
friend class Store;
7778
wasmtime_context_t *ptr;

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

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

18+
struct wasmtime_eqref;
19+
typedef struct wasmtime_eqref wasmtime_eqref_t;
20+
1821
/**
1922
* \typedef wasmtime_anyref_t
2023
* \brief Convenience alias for #wasmtime_anyref
@@ -77,6 +80,19 @@ static inline bool wasmtime_anyref_is_null(const wasmtime_anyref_t *ref) {
7780
WASM_API_EXTERN void wasmtime_anyref_clone(const wasmtime_anyref_t *anyref,
7881
wasmtime_anyref_t *out);
7982

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

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

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)