Skip to content

Commit 6ed4812

Browse files
committed
Add support for arrayrefs to the C and C++ APIs
Also array types and `ArrayRefPre`.
1 parent 083968d commit 6ed4812

File tree

5 files changed

+612
-3
lines changed

5 files changed

+612
-3
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: 161 additions & 1 deletion
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.
@@ -102,8 +103,14 @@ public:
102103
return AnyRef(out);
103104
}
104105

105-
// as_struct() defined after StructRef below.
106+
/// Returns `true` if this eqref is an arrayref.
107+
bool is_array(Store::Context cx) const {
108+
return wasmtime_eqref_is_array(cx.capi(), &val);
109+
}
110+
111+
// as_struct() and as_array() defined after StructRef/ArrayRef below.
106112
inline StructRef as_struct(Store::Context cx) const;
113+
inline ArrayRef as_array(Store::Context cx) const;
107114
};
108115

109116
/**
@@ -185,6 +192,7 @@ public:
185192
private:
186193
StructRefPre() = default;
187194
friend class StructRef;
195+
class ArrayRef;
188196
};
189197

190198
/**
@@ -284,6 +292,158 @@ inline StructRef EqRef::as_struct(Store::Context cx) const {
284292
return StructRef(out);
285293
}
286294

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

289449
#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
@@ -214,6 +214,7 @@ class Val {
214214
friend class Table;
215215
friend class Func;
216216
friend class StructRef;
217+
friend class ArrayRef;
217218

218219
wasmtime_val_t val;
219220

0 commit comments

Comments
 (0)