Skip to content

Commit 06432bd

Browse files
authored
Refactor reference types in the C/C++ API (#13154)
* Refactor reference types in the C/C++ API In updating `wasmtime-py` to Wasmtime 44 I noticed a few discrepancies in the C API, and then C++ API, in addition to what was found in #13149. Notably we had both `wasmtime_exn_t`/`Exn` and `wasmtime_exnref_t`/`ExnRef`. In sorting this out with some deeper fixes in #13149 I got stuck again in the rat's nest of circular definitions of classes in C++. In the end I gave up trying to incrementally improve things and decided to go with a larger refactoring. The goal here is to more cleanly separate out responsibilities and improve how circular definitions in C++ are handled: * All reference types now live in their own file. Reference types aren't in a mix of `val.h` or `gc.h` or `exn.h`, but instead each type lives in its own file. This means that `gc.h` is now gone and is replaced by `anyref.h`, `externref.h`, `exnref.h`, `eqref.h`, ... * C++ class definitions are split into separate headers from their main files. For example there's now `wasmtime/_anyref_class.hh` instead of just `wasmtime/anyref.hh`. This new method of resolving circular dependencies has some nice properties where headers that depend on the `AnyRef` type, for example, include `_anyref_class.h`. The `anyref.h` header then is able to include all other headers necessary for defining methods. Notably this means that methods now always live in the header of the type as opposed to sprinkled about in various locations. Note that this is applied to preexisting headers like `func.hh` and `store.hh` out of necessity, too. * Reference types in the C++ now share core methods via a macro to avoid duplicating code and to ensure they have a uniform API. * Some `#define`s for `WASMTIME_FEATURE_GC` guards were adjusted in a few locations. For example `wasmtime_valraw_t` no longer has GC fields when the GC proposal is disabled. * Reorganization was reflected on the Rust side as well. This means the previous `ref.rs` is now broken up into files such as `anyref.rs`. Some logic of core functions was also deduplicated within the `ref_wrapper!` macro. The old definitions of `wasmtime_exn_t` and `wasmtime_exnref_t` were also both merged. Overall this PR contains quite a lot of code movement and things were shuffled around, as well as adjustments to the `exnref` C API (removing duplication). Overall though the API is largely as it was before, although for the C++ API if individual headers are being included some new ones may need to be included to ensure all methods are defined. My hope is that the end result here is a bit more maintainable, types are easier to modify/extend, there's no longer any soups to deal with in C++, and everything should have a uniform API. * Add anyerf.h to list of all headers * Fix clippy prtest:full * Fix docs * Fix leak in test * More doc fixes * Review comments * Fix rebase conflict
1 parent 9d08671 commit 06432bd

57 files changed

Lines changed: 4017 additions & 3719 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

crates/c-api/include/wasmtime.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,11 +196,17 @@
196196
#include <wasmtime/conf.h>
197197
// clang-format off
198198
// IWYU pragma: begin_exports
199+
#include <wasmtime/anyref.h>
200+
#include <wasmtime/arrayref.h>
201+
#include <wasmtime/async.h>
202+
#include <wasmtime/component.h>
199203
#include <wasmtime/config.h>
200204
#include <wasmtime/engine.h>
205+
#include <wasmtime/eqref.h>
201206
#include <wasmtime/error.h>
202-
#include <wasmtime/exn.h>
207+
#include <wasmtime/exnref.h>
203208
#include <wasmtime/extern.h>
209+
#include <wasmtime/externref.h>
204210
#include <wasmtime/func.h>
205211
#include <wasmtime/global.h>
206212
#include <wasmtime/instance.h>
@@ -210,13 +216,13 @@
210216
#include <wasmtime/profiling.h>
211217
#include <wasmtime/sharedmemory.h>
212218
#include <wasmtime/store.h>
219+
#include <wasmtime/structref.h>
213220
#include <wasmtime/table.h>
214221
#include <wasmtime/tag.h>
215222
#include <wasmtime/trap.h>
223+
#include <wasmtime/types/arrayref.h>
224+
#include <wasmtime/types/structref.h>
216225
#include <wasmtime/val.h>
217-
#include <wasmtime/gc.h>
218-
#include <wasmtime/async.h>
219-
#include <wasmtime/component.h>
220226
#include <wasmtime/wat.h>
221227
// IWYU pragma: end_exports
222228
// clang-format on

crates/c-api/include/wasmtime.hh

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,23 @@
3535
#ifndef WASMTIME_HH
3636
#define WASMTIME_HH
3737

38+
#include <wasmtime/anyref.hh>
39+
#include <wasmtime/arrayref.hh>
3840
#include <wasmtime/config.hh>
3941
#include <wasmtime/engine.hh>
42+
#include <wasmtime/eqref.hh>
4043
#include <wasmtime/error.hh>
41-
#include <wasmtime/exn.hh>
44+
#include <wasmtime/exnref.hh>
4245
#include <wasmtime/extern.hh>
46+
#include <wasmtime/externref.hh>
4347
#include <wasmtime/func.hh>
44-
#include <wasmtime/gc.hh>
4548
#include <wasmtime/global.hh>
4649
#include <wasmtime/instance.hh>
4750
#include <wasmtime/linker.hh>
4851
#include <wasmtime/memory.hh>
4952
#include <wasmtime/module.hh>
5053
#include <wasmtime/store.hh>
54+
#include <wasmtime/structref.hh>
5155
#include <wasmtime/table.hh>
5256
#include <wasmtime/tag.hh>
5357
#include <wasmtime/trap.hh>
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#ifndef WASMTIME_ANYREF_CLASS_HH
2+
#define WASMTIME_ANYREF_CLASS_HH
3+
4+
#include <wasmtime/conf.h>
5+
6+
#ifdef WASMTIME_FEATURE_GC
7+
8+
#include <wasmtime/_store_class.hh>
9+
#include <wasmtime/anyref.h>
10+
#include <wasmtime/helpers.hh>
11+
12+
namespace wasmtime {
13+
14+
class ArrayRef;
15+
class EqRef;
16+
class StructRef;
17+
18+
/**
19+
* \brief Representation of a WebAssembly `anyref` value.
20+
*/
21+
class AnyRef {
22+
WASMTIME_TOP_REF_WRAPPER(AnyRef, wasmtime_anyref);
23+
24+
/// Creates a new `AnyRef` which is an `i31` with the given `value`,
25+
/// truncated if the upper bit is set.
26+
static AnyRef i31(Store::Context cx, uint32_t value) {
27+
wasmtime_anyref_t other;
28+
wasmtime_anyref_from_i31(cx.capi(), value, &other);
29+
return AnyRef(other);
30+
}
31+
32+
/// \brief If this is an `i31`, get the value zero-extended.
33+
std::optional<uint32_t> u31(Store::Context cx) const {
34+
uint32_t ret = 0;
35+
if (wasmtime_anyref_i31_get_u(cx.capi(), &raw, &ret))
36+
return ret;
37+
return std::nullopt;
38+
}
39+
40+
/// \brief If this is an `i31`, get the value sign-extended.
41+
std::optional<int32_t> i31(Store::Context cx) const {
42+
int32_t ret = 0;
43+
if (wasmtime_anyref_i31_get_s(cx.capi(), &raw, &ret))
44+
return ret;
45+
return std::nullopt;
46+
}
47+
48+
/// \brief Returns `true` if this anyref is an i31ref.
49+
bool is_i31(Store::Context cx) const {
50+
return wasmtime_anyref_is_i31(cx.capi(), &raw);
51+
}
52+
53+
/// \brief Returns `true` if this anyref is an eqref.
54+
inline bool is_eqref(Store::Context cx) const;
55+
56+
/// \brief Returns `true` if this anyref is a structref.
57+
inline bool is_struct(Store::Context cx) const;
58+
59+
/// \brief Returns `true` if this anyref is an arrayref.
60+
inline bool is_array(Store::Context cx) const;
61+
62+
/// \brief Downcast to eqref. Returns null eqref if not an eqref.
63+
inline std::optional<EqRef> as_eqref(Store::Context cx) const;
64+
65+
/// \brief Downcast to structref. Returns null structref if not a structref.
66+
inline std::optional<StructRef> as_struct(Store::Context cx) const;
67+
68+
/// \brief Downcast to arrayref. Returns null arrayref if not an arrayref.
69+
inline std::optional<ArrayRef> as_array(Store::Context cx) const;
70+
};
71+
72+
} // namespace wasmtime
73+
74+
#endif // WASMTIME_FEATURE_GC
75+
76+
#endif // WASMTIME_ANYREF_CLASS_HH
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#ifndef WASMTIME_ARRAYREF_CLASS_HH
2+
#define WASMTIME_ARRAYREF_CLASS_HH
3+
4+
#include <wasmtime/conf.h>
5+
6+
#ifdef WASMTIME_FEATURE_GC
7+
8+
#include <wasmtime/_store_class.hh>
9+
#include <wasmtime/arrayref.h>
10+
#include <wasmtime/types/arrayref.hh>
11+
12+
namespace wasmtime {
13+
14+
class Val;
15+
class AnyRef;
16+
class EqRef;
17+
18+
/**
19+
* \brief Pre-allocated array layout for fast allocation of array instances.
20+
*
21+
* Created from a ArrayType and a store context. Reusable for allocating
22+
* many array instances of the same type.
23+
*/
24+
class ArrayRefPre {
25+
friend class ArrayRef;
26+
WASMTIME_OWN_WRAPPER(ArrayRefPre, wasmtime_array_ref_pre)
27+
28+
public:
29+
/// Create a new array pre-allocator.
30+
static ArrayRefPre create(Store::Context cx, const ArrayType &ty) {
31+
auto *raw = wasmtime_array_ref_pre_new(cx.capi(), ty.capi());
32+
ArrayRefPre pre(raw);
33+
return pre;
34+
}
35+
};
36+
37+
/**
38+
* \brief Representation of a WebAssembly `arrayref` value.
39+
*
40+
* An `arrayref` is a reference to a GC array instance. It is a subtype
41+
* of `eqref` and `anyref`.
42+
*/
43+
class ArrayRef {
44+
WASMTIME_REF_WRAPPER(ArrayRef, wasmtime_arrayref)
45+
46+
public:
47+
/// Allocate a new array with all elements set to the same value.
48+
static Result<ArrayRef> create(Store::Context cx, const ArrayRefPre &pre,
49+
const Val &elem, uint32_t len);
50+
51+
/// Get the length of the array.
52+
Result<uint32_t> len(Store::Context cx) const {
53+
uint32_t out;
54+
auto *err = wasmtime_arrayref_len(cx.capi(), &raw, &out);
55+
if (err)
56+
return Result<uint32_t>(Error(err));
57+
return Result<uint32_t>(out);
58+
}
59+
60+
/// Read an element from the array.
61+
Result<Val> get(Store::Context cx, uint32_t index) const;
62+
63+
/// Set an element of the array.
64+
Result<std::monostate> set(Store::Context cx, uint32_t index,
65+
const Val &value) const;
66+
67+
/// Upcast to anyref.
68+
AnyRef to_anyref() const;
69+
70+
/// Upcast to eqref.
71+
EqRef to_eqref() const;
72+
};
73+
74+
} // namespace wasmtime
75+
76+
#endif // WASMTIME_FEATURE_GC
77+
78+
#endif // WASMTIME_ARRAYREF_CLASS_HH
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#ifndef WASMTIME_EQREF_CLASS_HH
2+
#define WASMTIME_EQREF_CLASS_HH
3+
4+
#include <wasmtime/conf.h>
5+
6+
#ifdef WASMTIME_FEATURE_GC
7+
8+
#include <wasmtime/_store_class.hh>
9+
#include <wasmtime/eqref.h>
10+
#include <wasmtime/helpers.hh>
11+
12+
namespace wasmtime {
13+
14+
class AnyRef;
15+
class StructRef;
16+
class ArrayRef;
17+
18+
/**
19+
* \brief Representation of a WebAssembly `eqref` value.
20+
*
21+
* An `eqref` is a reference to a GC object that supports equality testing.
22+
* Subtypes include `structref`, `arrayref`, and `i31ref`.
23+
*
24+
* Like all GC references, `EqRef` values are rooted in a `Store` and must be
25+
* unrooted (by destruction or move) to allow garbage collection.
26+
*/
27+
class EqRef {
28+
WASMTIME_REF_WRAPPER(EqRef, wasmtime_eqref);
29+
30+
public:
31+
/// Create an `eqref` from an i31 value.
32+
static EqRef from_i31(Store::Context cx, uint32_t val) {
33+
wasmtime_eqref_t out;
34+
wasmtime_eqref_from_i31(cx.capi(), val, &out);
35+
return EqRef(out);
36+
}
37+
38+
/// Returns `true` if this eqref is an i31ref.
39+
bool is_i31(Store::Context cx) const {
40+
return wasmtime_eqref_is_i31(cx.capi(), &raw);
41+
}
42+
43+
/// Get the i31 value as an unsigned 32-bit integer.
44+
/// Returns `std::nullopt` if this eqref is not an i31ref.
45+
std::optional<uint32_t> i31_get_u(Store::Context cx) const {
46+
uint32_t dst;
47+
if (wasmtime_eqref_i31_get_u(cx.capi(), &raw, &dst))
48+
return dst;
49+
return std::nullopt;
50+
}
51+
52+
/// Get the i31 value as a signed 32-bit integer.
53+
/// Returns `std::nullopt` if this eqref is not an i31ref.
54+
std::optional<int32_t> i31_get_s(Store::Context cx) const {
55+
int32_t dst;
56+
if (wasmtime_eqref_i31_get_s(cx.capi(), &raw, &dst))
57+
return dst;
58+
return std::nullopt;
59+
}
60+
61+
/// Returns `true` if this eqref is a structref.
62+
bool is_struct(Store::Context cx) const {
63+
return wasmtime_eqref_is_struct(cx.capi(), &raw);
64+
}
65+
66+
/// Returns `true` if this eqref is an arrayref.
67+
bool is_array(Store::Context cx) const {
68+
return wasmtime_eqref_is_array(cx.capi(), &raw);
69+
}
70+
71+
/// Upcast this `eqref` to an `anyref`.
72+
AnyRef to_anyref() const;
73+
74+
/// Downcast this `eqref` into a `structref`.
75+
//
76+
// as_struct() defined after StructRef below.
77+
StructRef as_struct(Store::Context cx) const;
78+
79+
/// Downcast this `eqref` into an `arrayref`.
80+
//
81+
// as_array() defined after ArrayRef below.
82+
ArrayRef as_array(Store::Context cx) const;
83+
};
84+
85+
} // namespace wasmtime
86+
87+
#endif // WASMTIME_FEATURE_GC
88+
89+
#endif // WASMTIME_EQREF_CLASS_HH
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#ifndef WASMTIME_EXNREF_CLASS_HH
2+
#define WASMTIME_EXNREF_CLASS_HH
3+
4+
#include <wasmtime/conf.h>
5+
6+
#ifdef WASMTIME_FEATURE_GC
7+
8+
#include <vector>
9+
#include <wasmtime/_store_class.hh>
10+
#include <wasmtime/error.hh>
11+
#include <wasmtime/exnref.h>
12+
#include <wasmtime/helpers.hh>
13+
#include <wasmtime/tag.hh>
14+
15+
namespace wasmtime {
16+
17+
class Val;
18+
19+
/**
20+
* \brief A WebAssembly exception object.
21+
*
22+
* Exception objects carry a tag and a set of field values. They are
23+
* allocated on the GC heap within a store.
24+
*
25+
* This type owns its underlying `wasmtime_exnref_t` handle. When it goes out
26+
* of scope the handle is freed.
27+
*/
28+
class ExnRef {
29+
WASMTIME_TOP_REF_WRAPPER(ExnRef, wasmtime_exnref);
30+
31+
/**
32+
* \brief Create a new exception object.
33+
*
34+
* \param cx the store in which to allocate the exception
35+
* \param tag the tag to associate with this exception
36+
* \param fields the field values matching the tag's payload signature
37+
*/
38+
static Result<ExnRef> create(Store::Context cx, const Tag &tag,
39+
const std::vector<Val> &fields);
40+
41+
/// Returns the tag associated with this exception.
42+
Result<Tag> tag(Store::Context cx) const {
43+
wasmtime_tag_t tag;
44+
auto *error = wasmtime_exnref_tag(cx.capi(), &raw, &tag);
45+
if (error != nullptr) {
46+
return Error(error);
47+
}
48+
return Tag(tag);
49+
}
50+
51+
/// Returns the number of fields in this exception.
52+
size_t field_count(Store::Context cx) const {
53+
return wasmtime_exnref_field_count(cx.capi(), &raw);
54+
}
55+
56+
/// Reads a field value by index.
57+
Result<Val> field(Store::Context cx, size_t index) const;
58+
};
59+
60+
} // namespace wasmtime
61+
62+
#endif // WASMTIME_FEATURE_GC
63+
64+
#endif // WASMTIME_EXNREF_CLASS_HH

0 commit comments

Comments
 (0)