Skip to content

Commit fc8dc5d

Browse files
authored
Add support for arrayrefs to the C and C++ APIs (#12916)
* Add support for `arrayref`s to the C and C++ APIs Also array types and `ArrayRefPre`. * add docs and address similar issues from structref PR's review * fix doc build
1 parent 0bc447b commit fc8dc5d

File tree

5 files changed

+614
-2
lines changed

5 files changed

+614
-2
lines changed

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

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,199 @@ WASM_API_EXTERN bool wasmtime_eqref_as_struct(wasmtime_context_t *context,
342342
const wasmtime_eqref_t *eqref,
343343
wasmtime_structref_t *out);
344344

345+
/**
346+
* \brief An opaque handle to a WebAssembly array type definition.
347+
*
348+
* An array type describes the element type of an array. It is used to create a
349+
* #wasmtime_array_ref_pre_t, which can then allocate array instances.
350+
*
351+
* Owned. Must be deleted with #wasmtime_array_type_delete.
352+
*/
353+
typedef struct wasmtime_array_type wasmtime_array_type_t;
354+
355+
/**
356+
* \brief Create a new array type.
357+
*
358+
* \param engine The engine to register the type with.
359+
* \param field The element type descriptor.
360+
*
361+
* \return Returns a new array type.
362+
*/
363+
WASM_API_EXTERN wasmtime_array_type_t *
364+
wasmtime_array_type_new(const wasm_engine_t *engine,
365+
const wasmtime_field_type_t *field);
366+
367+
/**
368+
* \brief Delete an array type.
369+
*/
370+
WASM_API_EXTERN void wasmtime_array_type_delete(wasmtime_array_type_t *ty);
371+
372+
/**
373+
* \brief An opaque pre-allocated array layout for fast allocation.
374+
*
375+
* Created from a #wasmtime_array_type_t and a store context. Reusable for
376+
* allocating many array instances of the same type.
377+
*
378+
* Owned. Must be deleted with #wasmtime_array_ref_pre_delete.
379+
*/
380+
typedef struct wasmtime_array_ref_pre wasmtime_array_ref_pre_t;
381+
382+
/**
383+
* \brief Create a new array pre-allocator.
384+
*
385+
* \param context The store context.
386+
* \param ty The array type (not consumed; caller retains ownership).
387+
*
388+
* \return Returns a new array ref pre-allocator.
389+
*/
390+
WASM_API_EXTERN wasmtime_array_ref_pre_t *
391+
wasmtime_array_ref_pre_new(wasmtime_context_t *context,
392+
const wasmtime_array_type_t *ty);
393+
394+
/**
395+
* \brief Delete an array pre-allocator.
396+
*/
397+
WASM_API_EXTERN void
398+
wasmtime_array_ref_pre_delete(wasmtime_array_ref_pre_t *pre);
399+
400+
/**
401+
* \typedef wasmtime_arrayref_t
402+
* \brief Convenience alias for #wasmtime_arrayref
403+
*
404+
* \struct wasmtime_arrayref
405+
* \brief A WebAssembly `arrayref` value.
406+
*
407+
* This structure represents a reference to a GC array. It is a subtype of
408+
* `eqref` and `anyref`.
409+
*
410+
* Values must be explicitly unrooted via #wasmtime_arrayref_unroot.
411+
*/
412+
typedef struct wasmtime_arrayref {
413+
/// Internal metadata.
414+
uint64_t store_id;
415+
/// Internal to Wasmtime.
416+
uint32_t __private1;
417+
/// Internal to Wasmtime.
418+
uint32_t __private2;
419+
/// Internal to Wasmtime.
420+
void *__private3;
421+
} wasmtime_arrayref_t;
422+
423+
/// \brief Initialize the `ref` to a null `arrayref` value.
424+
static inline void wasmtime_arrayref_set_null(wasmtime_arrayref_t *ref) {
425+
ref->store_id = 0;
426+
}
427+
428+
/// \brief Returns whether the provided `ref` is a null `arrayref` value.
429+
static inline bool wasmtime_arrayref_is_null(const wasmtime_arrayref_t *ref) {
430+
return ref->store_id == 0;
431+
}
432+
433+
/**
434+
* \brief Allocate a new array instance.
435+
*
436+
* All elements are initialized to the same value.
437+
*
438+
* \param context The store context.
439+
* \param pre The array pre-allocator.
440+
* \param elem The initial element value.
441+
* \param len The number of elements.
442+
* \param out Receives the new arrayref on success.
443+
*
444+
* \return NULL on success, or a #wasmtime_error_t on failure.
445+
*/
446+
WASM_API_EXTERN wasmtime_error_t *wasmtime_arrayref_new(
447+
wasmtime_context_t *context, const wasmtime_array_ref_pre_t *pre,
448+
const wasmtime_val_t *elem, uint32_t len, wasmtime_arrayref_t *out);
449+
450+
/**
451+
* \brief Clone an `arrayref`, creating a new root.
452+
*/
453+
WASM_API_EXTERN void
454+
wasmtime_arrayref_clone(const wasmtime_arrayref_t *arrayref,
455+
wasmtime_arrayref_t *out);
456+
457+
/**
458+
* \brief Unroot an `arrayref` to allow garbage collection.
459+
*/
460+
WASM_API_EXTERN void wasmtime_arrayref_unroot(wasmtime_arrayref_t *ref);
461+
462+
/**
463+
* \brief Upcast an `arrayref` to an `anyref`.
464+
*/
465+
WASM_API_EXTERN void
466+
wasmtime_arrayref_to_anyref(const wasmtime_arrayref_t *arrayref,
467+
wasmtime_anyref_t *out);
468+
469+
/**
470+
* \brief Upcast an `arrayref` to an `eqref`.
471+
*/
472+
WASM_API_EXTERN void
473+
wasmtime_arrayref_to_eqref(const wasmtime_arrayref_t *arrayref,
474+
wasmtime_eqref_t *out);
475+
476+
/**
477+
* \brief Get the length of an array.
478+
*
479+
* \param context The store context.
480+
* \param arrayref The array (not consumed).
481+
* \param out Receives the length on success.
482+
*
483+
* \return NULL on success, or a #wasmtime_error_t on failure.
484+
*/
485+
WASM_API_EXTERN wasmtime_error_t *
486+
wasmtime_arrayref_len(wasmtime_context_t *context,
487+
const wasmtime_arrayref_t *arrayref, uint32_t *out);
488+
489+
/**
490+
* \brief Read an element from an array.
491+
*
492+
* \param context The store context.
493+
* \param arrayref The array (not consumed).
494+
* \param index The element index.
495+
* \param out Receives the element value on success.
496+
*
497+
* \return NULL on success, or a #wasmtime_error_t on failure.
498+
*/
499+
WASM_API_EXTERN wasmtime_error_t *
500+
wasmtime_arrayref_get(wasmtime_context_t *context,
501+
const wasmtime_arrayref_t *arrayref, uint32_t index,
502+
wasmtime_val_t *out);
503+
504+
/**
505+
* \brief Set an element of an array.
506+
*
507+
* \param context The store context.
508+
* \param arrayref The array (not consumed).
509+
* \param index The element index.
510+
* \param val The value to write.
511+
*
512+
* \return NULL on success, or a #wasmtime_error_t on failure.
513+
*/
514+
WASM_API_EXTERN wasmtime_error_t *
515+
wasmtime_arrayref_set(wasmtime_context_t *context,
516+
const wasmtime_arrayref_t *arrayref, uint32_t index,
517+
const wasmtime_val_t *val);
518+
519+
/**
520+
* \brief Test whether an `eqref` is an `arrayref`.
521+
*
522+
* Returns `false` for null references.
523+
*/
524+
WASM_API_EXTERN bool wasmtime_eqref_is_array(wasmtime_context_t *context,
525+
const wasmtime_eqref_t *eqref);
526+
527+
/**
528+
* \brief Downcast an `eqref` to an `arrayref`.
529+
*
530+
* If the given `eqref` is an `arrayref`, a new root for it is stored in `out`
531+
* and `true` is returned. Otherwise `false` is returned and `out` is set to
532+
* null.
533+
*/
534+
WASM_API_EXTERN bool wasmtime_eqref_as_array(wasmtime_context_t *context,
535+
const wasmtime_eqref_t *eqref,
536+
wasmtime_arrayref_t *out);
537+
345538
#ifdef __cplusplus
346539
} // extern "C"
347540
#endif

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

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
namespace wasmtime {
1515

1616
class StructRef;
17+
class ArrayRef;
1718

1819
/**
1920
* \brief Representation of a WebAssembly `eqref` value.
@@ -95,6 +96,11 @@ public:
9596
return wasmtime_eqref_is_struct(cx.capi(), &val);
9697
}
9798

99+
/// Returns `true` if this eqref is an arrayref.
100+
bool is_array(Store::Context cx) const {
101+
return wasmtime_eqref_is_array(cx.capi(), &val);
102+
}
103+
98104
/// Upcast this `eqref` to an `anyref`.
99105
AnyRef to_anyref() const {
100106
wasmtime_anyref_t out;
@@ -106,6 +112,11 @@ public:
106112
//
107113
// as_struct() defined after StructRef below.
108114
inline StructRef as_struct(Store::Context cx) const;
115+
116+
/// Downcast this `eqref` into an `arrayref`.
117+
//
118+
// as_array() defined after ArrayRef below.
119+
inline ArrayRef as_array(Store::Context cx) const;
109120
};
110121

111122
/**
@@ -282,6 +293,158 @@ inline StructRef EqRef::as_struct(Store::Context cx) const {
282293
return StructRef(out);
283294
}
284295

296+
/**
297+
* \brief Owned handle to a WebAssembly array type definition.
298+
*/
299+
class ArrayType {
300+
struct Deleter {
301+
void operator()(wasmtime_array_type_t *p) const {
302+
wasmtime_array_type_delete(p);
303+
}
304+
};
305+
std::unique_ptr<wasmtime_array_type_t, Deleter> ptr;
306+
307+
public:
308+
/// Create a new array type with the given element type.
309+
static ArrayType create(const Engine &engine, const FieldType &field) {
310+
static_assert(sizeof(FieldType) == sizeof(wasmtime_field_type_t));
311+
auto *raw = wasmtime_array_type_new(
312+
engine.capi(), reinterpret_cast<const wasmtime_field_type_t *>(&field));
313+
ArrayType ty;
314+
ty.ptr.reset(raw);
315+
return ty;
316+
}
317+
318+
/// Get the underlying C pointer (non-owning).
319+
const wasmtime_array_type_t *capi() const { return ptr.get(); }
320+
321+
private:
322+
ArrayType() = default;
323+
friend class ArrayRefPre;
324+
};
325+
326+
/**
327+
* \brief Pre-allocated array layout for fast allocation of array instances.
328+
*
329+
* Created from a ArrayType and a store context. Reusable for allocating
330+
* many array instances of the same type.
331+
*/
332+
class ArrayRefPre {
333+
friend class ArrayRef;
334+
WASMTIME_OWN_WRAPPER(ArrayRefPre, wasmtime_array_ref_pre)
335+
336+
public:
337+
/// Create a new array pre-allocator.
338+
static ArrayRefPre create(Store::Context cx, const ArrayType &ty) {
339+
auto *raw = wasmtime_array_ref_pre_new(cx.capi(), ty.capi());
340+
ArrayRefPre pre(raw);
341+
return pre;
342+
}
343+
};
344+
345+
/**
346+
* \brief Representation of a WebAssembly `arrayref` value.
347+
*
348+
* An `arrayref` is a reference to a GC array instance. It is a subtype
349+
* of `eqref` and `anyref`.
350+
*/
351+
class ArrayRef {
352+
friend class EqRef;
353+
friend class Val;
354+
friend class AnyRef;
355+
356+
wasmtime_arrayref_t val;
357+
358+
public:
359+
/// Create a `ArrayRef` from its C-API representation.
360+
explicit ArrayRef(wasmtime_arrayref_t val) : val(val) {}
361+
362+
/// Clone a `ArrayRef`.
363+
ArrayRef(const ArrayRef &other) { wasmtime_arrayref_clone(&other.val, &val); }
364+
365+
/// Clone a `ArrayRef` into this one.
366+
ArrayRef &operator=(const ArrayRef &other) {
367+
wasmtime_arrayref_unroot(&val);
368+
wasmtime_arrayref_clone(&other.val, &val);
369+
return *this;
370+
}
371+
372+
/// Move a `ArrayRef`.
373+
ArrayRef(ArrayRef &&other) {
374+
val = other.val;
375+
wasmtime_arrayref_set_null(&other.val);
376+
}
377+
378+
/// Move a `ArrayRef` into this one.
379+
ArrayRef &operator=(ArrayRef &&other) {
380+
wasmtime_arrayref_unroot(&val);
381+
val = other.val;
382+
wasmtime_arrayref_set_null(&other.val);
383+
return *this;
384+
}
385+
386+
/// Unroot this `ArrayRef`.
387+
~ArrayRef() { wasmtime_arrayref_unroot(&val); }
388+
389+
/// Allocate a new array with all elements set to the same value.
390+
static Result<ArrayRef> create(Store::Context cx, const ArrayRefPre &pre,
391+
const Val &elem, uint32_t len) {
392+
wasmtime_arrayref_t out;
393+
auto *err =
394+
wasmtime_arrayref_new(cx.capi(), pre.capi(), &elem.val, len, &out);
395+
if (err)
396+
return Result<ArrayRef>(Error(err));
397+
return Result<ArrayRef>(ArrayRef(out));
398+
}
399+
400+
/// Get the length of the array.
401+
Result<uint32_t> len(Store::Context cx) const {
402+
uint32_t out;
403+
auto *err = wasmtime_arrayref_len(cx.capi(), &val, &out);
404+
if (err)
405+
return Result<uint32_t>(Error(err));
406+
return Result<uint32_t>(out);
407+
}
408+
409+
/// Read an element from the array.
410+
Result<Val> get(Store::Context cx, uint32_t index) const {
411+
wasmtime_val_t out;
412+
auto *err = wasmtime_arrayref_get(cx.capi(), &val, index, &out);
413+
if (err)
414+
return Result<Val>(Error(err));
415+
return Result<Val>(Val(out));
416+
}
417+
418+
/// Set an element of the array.
419+
Result<std::monostate> set(Store::Context cx, uint32_t index,
420+
const Val &value) const {
421+
auto *err = wasmtime_arrayref_set(cx.capi(), &val, index, &value.val);
422+
if (err)
423+
return Result<std::monostate>(Error(err));
424+
return Result<std::monostate>(std::monostate{});
425+
}
426+
427+
/// Upcast to anyref.
428+
AnyRef to_anyref() const {
429+
wasmtime_anyref_t out;
430+
wasmtime_arrayref_to_anyref(&val, &out);
431+
return AnyRef(out);
432+
}
433+
434+
/// Upcast to eqref.
435+
EqRef to_eqref() const {
436+
wasmtime_eqref_t out;
437+
wasmtime_arrayref_to_eqref(&val, &out);
438+
return EqRef(out);
439+
}
440+
};
441+
442+
inline ArrayRef EqRef::as_array(Store::Context cx) const {
443+
wasmtime_arrayref_t out;
444+
wasmtime_eqref_as_array(cx.capi(), &val, &out);
445+
return ArrayRef(out);
446+
}
447+
285448
} // namespace wasmtime
286449

287450
#endif // WASMTIME_GC_HH

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ class Val {
215215
friend class Func;
216216
friend class Exn;
217217
friend class StructRef;
218+
friend class ArrayRef;
218219

219220
wasmtime_val_t val;
220221

0 commit comments

Comments
 (0)