diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h index 3573e794633d..f585b1a78ec8 100644 --- a/crates/c-api/include/wasmtime.h +++ b/crates/c-api/include/wasmtime.h @@ -205,7 +205,6 @@ #include #include #include -#include #include // IWYU pragma: end_exports // clang-format on diff --git a/crates/c-api/include/wasmtime/component.hh b/crates/c-api/include/wasmtime/component.hh index c2ef6a3b6dd9..422671d2bf90 100644 --- a/crates/c-api/include/wasmtime/component.hh +++ b/crates/c-api/include/wasmtime/component.hh @@ -4,5 +4,9 @@ #define WASMTIME_COMPONENT_HH #include +#include +#include +#include +#include #endif // WASMTIME_COMPONENT_HH diff --git a/crates/c-api/include/wasmtime/component/component.hh b/crates/c-api/include/wasmtime/component/component.hh index 8c60ac8875eb..234d67ca996f 100644 --- a/crates/c-api/include/wasmtime/component/component.hh +++ b/crates/c-api/include/wasmtime/component/component.hh @@ -2,8 +2,8 @@ * \file wasmtime/component/component.hh */ -#ifndef WASMTIME_COMPONENT_HH -#define WASMTIME_COMPONENT_HH +#ifndef WASMTIME_COMPONENT_COMPONENT_HH +#define WASMTIME_COMPONENT_COMPONENT_HH #include @@ -39,9 +39,10 @@ class ExportIndex { std::unique_ptr ptr; - ExportIndex(wasmtime_component_export_index_t *raw) : ptr(raw) {} - public: + /// \brief Constructs an ExportIndex from the underlying C API struct. + explicit ExportIndex(wasmtime_component_export_index_t *raw) : ptr(raw) {} + /// Copies another index into this one. ExportIndex(const ExportIndex &other) : ptr(wasmtime_component_export_index_clone(other.ptr.get())) {} @@ -227,4 +228,4 @@ public: #endif // WASMTIME_FEATURE_COMPONENT_MODEL -#endif // WASMTIME_COMPONENT_HH +#endif // WASMTIME_COMPONENT_COMPONENT_HH diff --git a/crates/c-api/include/wasmtime/component/func.hh b/crates/c-api/include/wasmtime/component/func.hh new file mode 100644 index 000000000000..e0ed99cb4782 --- /dev/null +++ b/crates/c-api/include/wasmtime/component/func.hh @@ -0,0 +1,64 @@ +/// \file wasmtime/component/func.hh + +#ifndef WASMTIME_COMPONENT_FUNC_HH +#define WASMTIME_COMPONENT_FUNC_HH + +#include + +#ifdef WASMTIME_FEATURE_COMPONENT_MODEL + +#include +#include +#include +#include +#include +#include + +namespace wasmtime { +namespace component { + +/** + * \brief Class representing an instantiated WebAssembly component. + */ +class Func { + wasmtime_component_func_t func; + +public: + /// \brief Constructs an Func from the underlying C API struct. + explicit Func(const wasmtime_component_func_t &func) : func(func) {} + + /// \brief Returns the underlying C API pointer. + const wasmtime_component_func_t *capi() const { return &func; } + + /// \brief Invokes this component function with the provided `args` and the + /// results are placed in `results`. + Result call(Store::Context cx, Span args, + Span results) const { + wasmtime_error_t *error = wasmtime_component_func_call( + &func, cx.capi(), Val::to_capi(args.data()), args.size(), + Val::to_capi(results.data()), results.size()); + if (error != nullptr) { + return Error(error); + } + return std::monostate(); + } + + /** + * \brief Invokes the `post-return` canonical ABI option, if specified. + */ + Result post_return(Store::Context cx) const { + wasmtime_error_t *error = + wasmtime_component_func_post_return(&func, cx.capi()); + if (error != nullptr) { + return Error(error); + } + return std::monostate(); + } +}; + +} // namespace component +} // namespace wasmtime + +#endif // WASMTIME_FEATURE_COMPONENT_MODEL + +#endif // WASMTIME_COMPONENT_FUNC_H diff --git a/crates/c-api/include/wasmtime/component/instance.hh b/crates/c-api/include/wasmtime/component/instance.hh new file mode 100644 index 000000000000..07f7f91c8996 --- /dev/null +++ b/crates/c-api/include/wasmtime/component/instance.hh @@ -0,0 +1,70 @@ +/// \file wasmtime/component/instance.hh + +#ifndef WASMTIME_COMPONENT_INSTANCE_HH +#define WASMTIME_COMPONENT_INSTANCE_HH + +#include + +#ifdef WASMTIME_FEATURE_COMPONENT_MODEL + +#include +#include +#include +#include +#include + +namespace wasmtime { +namespace component { + +/** + * \brief Class representing an instantiated WebAssembly component. + */ +class Instance { + wasmtime_component_instance_t instance; + +public: + /// \brief Constructs an Instance from the underlying C API struct. + explicit Instance(const wasmtime_component_instance_t &inst) + : instance(inst) {} + + /// \brief Looks up an exported item from this instance by name, returning the + /// index at which it can be found. + /// + /// The returned `ExportIndex` references the underlying item within this + /// instance which can then be accessed via that index specifically. The + /// `instance` provided as an argument to this function is the containing + /// export instance, if any, that `name` is looked up under. + std::optional get_export_index(Store::Context cx, + const ExportIndex *instance, + std::string_view name) const { + wasmtime_component_export_index_t *ret = + wasmtime_component_instance_get_export_index( + &this->instance, cx.capi(), instance ? instance->capi() : nullptr, + name.data(), name.size()); + if (ret == nullptr) { + return std::nullopt; + } + return ExportIndex(ret); + } + + /// \brief Looks up an exported function by its export index. + std::optional get_func(Store::Context cx, + const ExportIndex &index) const { + wasmtime_component_func_t ret; + bool found = wasmtime_component_instance_get_func(&instance, cx.capi(), + index.capi(), &ret); + if (!found) + return std::nullopt; + return Func(ret); + } + + /// \brief Returns the underlying C API pointer. + const wasmtime_component_instance_t *capi() const { return &instance; } +}; + +} // namespace component +} // namespace wasmtime + +#endif // WASMTIME_FEATURE_COMPONENT_MODEL + +#endif // WASMTIME_COMPONENT_INSTANCE_H diff --git a/crates/c-api/include/wasmtime/component/linker.h b/crates/c-api/include/wasmtime/component/linker.h index 5fb1582c05b2..c61d98df5aa1 100644 --- a/crates/c-api/include/wasmtime/component/linker.h +++ b/crates/c-api/include/wasmtime/component/linker.h @@ -34,6 +34,16 @@ typedef struct wasmtime_component_linker_instance_t WASM_API_EXTERN wasmtime_component_linker_t * wasmtime_component_linker_new(const wasm_engine_t *engine); +/** + * \brief Configures whether this linker allows later definitions to shadow + * previous definitions. + * + * By default this setting is `false`. + */ +WASM_API_EXTERN void +wasmtime_component_linker_allow_shadowing(wasmtime_component_linker_t *linker, + bool allow); + /** * \brief Returns the "root instance" of this linker, used to define names into * the root namespace. diff --git a/crates/c-api/include/wasmtime/component/linker.hh b/crates/c-api/include/wasmtime/component/linker.hh new file mode 100644 index 000000000000..c63bd154ed28 --- /dev/null +++ b/crates/c-api/include/wasmtime/component/linker.hh @@ -0,0 +1,202 @@ +/// \file wasmtime/component/linker.hh + +#ifndef WASMTIME_COMPONENT_LINKER_HH +#define WASMTIME_COMPONENT_LINKER_HH + +#include + +#ifdef WASMTIME_FEATURE_COMPONENT_MODEL + +#include +#include +#include +#include +#include +#include +#include + +namespace wasmtime { +namespace component { + +/** + * \brief Helper class for linking modules together with name-based resolution. + * + * This class is used for easily instantiating `Module`s by defining names into + * the linker and performing name-based resolution during instantiation. A + * `Linker` can also be used to link in WASI functions to instantiate a module. + */ +class LinkerInstance { + struct deleter { + void operator()(wasmtime_component_linker_instance_t *p) const { + wasmtime_component_linker_instance_delete(p); + } + }; + + std::unique_ptr ptr; + +public: + /// Creates a new linker instance from the given C API pointer. + explicit LinkerInstance(wasmtime_component_linker_instance_t *ptr) + : ptr(ptr) {} + + /// \brief Returns the underlying C API pointer. + const wasmtime_component_linker_instance_t *capi() const { return ptr.get(); } + + /// \brief Returns the underlying C API pointer. + wasmtime_component_linker_instance_t *capi() { return ptr.get(); } + + /** + * \brief Adds a module to this linker instance under the specified name. + */ + Result add_module(std::string_view name, Module &module) { + wasmtime_error_t *error = wasmtime_component_linker_instance_add_module( + ptr.get(), name.data(), name.size(), module.capi()); + if (error != nullptr) { + return Error(error); + } + return std::monostate(); + } + + /** + * \brief Adds an new instance to this linker instance under the specified + * name. + * + * Note that this `LinkerInstance` can no longer be used until the returned + * `LinkerInstance` is dropped. + */ + Result add_instance(std::string_view name) { + wasmtime_component_linker_instance_t *ret = nullptr; + wasmtime_error_t *error = wasmtime_component_linker_instance_add_instance( + ptr.get(), name.data(), name.size(), &ret); + if (error != nullptr) { + return Error(error); + } + return LinkerInstance(ret); + } + +private: + template + static wasmtime_error_t * + raw_callback(void *env, wasmtime_context_t *store, + const wasmtime_component_val_t *args, size_t nargs, + wasmtime_component_val_t *results, size_t nresults) { + static_assert(alignof(Val) == alignof(wasmtime_component_val_t)); + static_assert(sizeof(Val) == sizeof(wasmtime_component_val_t)); + F *func = reinterpret_cast(env); + Span args_span(Val::from_capi(args), nargs); + Span results_span(Val::from_capi(results), nresults); + Result result = + (*func)(Store::Context(store), args_span, results_span); + if (!result) { + return result.err().release(); + } + return nullptr; + } + + template static void raw_finalize(void *env) { + std::unique_ptr ptr(reinterpret_cast(env)); + } + +public: + /// \brief Defines a function within this linker instance. + template , F, Store::Context, + Span, Span>, + bool> = true> + Result add_func(std::string_view name, F &&f) { + auto *error = wasmtime_component_linker_instance_add_func( + ptr.get(), name.data(), name.length(), + raw_callback>, + std::make_unique>(std::forward(f)) + .release(), + raw_finalize>); + + if (error != nullptr) { + return Error(error); + } + + return std::monostate(); + } +}; + +/** + * \brief Class used to instantiate a `Component` into an instance. + */ +class Linker { + struct deleter { + void operator()(wasmtime_component_linker_t *p) const { + wasmtime_component_linker_delete(p); + } + }; + + std::unique_ptr ptr; + +public: + /// Creates a new linker which will instantiate in the given engine. + explicit Linker(Engine &engine) + : ptr(wasmtime_component_linker_new(engine.capi())) {} + + /** + * \brief Gets the "root" instance of this linker which can be used to define + * items into the linker under the top-level namespace. + * + * This `Linker` cannot be used while the returned `LinkerInstance` is in + * scope. To use more methods on this `Linker` it's required that the instance + * returned here is dropped first. + */ + LinkerInstance root() { + wasmtime_component_linker_instance_t *instance_capi = + wasmtime_component_linker_root(ptr.get()); + return LinkerInstance(instance_capi); + } + + /// Configures whether shadowing previous names is allowed or not. + /// + /// By default shadowing is not allowed. + void allow_shadowing(bool allow) { + wasmtime_component_linker_allow_shadowing(ptr.get(), allow); + } + + /// \brief Instantiates the given component within this linker. + Result instantiate(Store::Context cx, Component &component) { + wasmtime_component_instance_t ret; + wasmtime_error_t *error = wasmtime_component_linker_instantiate( + ptr.get(), cx.capi(), component.capi(), &ret); + if (error != nullptr) { + return Error(error); + } + return Instance(ret); + } + +#ifdef WASMTIME_FEATURE_WASI + /** + * \brief Adds WASIp2 API definitions to this linker. + * + * This will use the WASIp2 API definitions in Wasmtime to this linker. Note + * that this adds *synchronous* versions of WASIp2 definitions which will + * block the caller when invoked. Internally this will use Wasmtime's + * default async runtime implemented with Tokio to handle async I/O. + */ + Result add_wasip2() { + wasmtime_error_t *error = wasmtime_component_linker_add_wasip2(ptr.get()); + if (error != nullptr) { + return Error(error); + } + return std::monostate(); + } +#endif // WASMTIME_FEATURE_WASI + + /// \brief Returns the underlying C API pointer. + const wasmtime_component_linker_t *capi() const { return ptr.get(); } + + /// \brief Returns the underlying C API pointer. + wasmtime_component_linker_t *capi() { return ptr.get(); } +}; + +} // namespace component +} // namespace wasmtime + +#endif // WASMTIME_FEATURE_COMPONENT_MODEL + +#endif // WASMTIME_COMPONENT_LINKER_H diff --git a/crates/c-api/include/wasmtime/component/val.h b/crates/c-api/include/wasmtime/component/val.h index 675889d46384..237353488595 100644 --- a/crates/c-api/include/wasmtime/component/val.h +++ b/crates/c-api/include/wasmtime/component/val.h @@ -94,7 +94,8 @@ struct wasmtime_component_valrecord_entry; } name##_t; \ \ /** \brief Create vec from \p ptr and \p size */ \ - WASM_API_EXTERN void name##_new(name##_t *out, size_t size, type *ptr); \ + WASM_API_EXTERN void name##_new(name##_t *out, size_t size, \ + const type *ptr); \ /** \brief Create an empty vec */ \ WASM_API_EXTERN void name##_new_empty(name##_t *out); \ /** \brief Create a vec with length \p size */ \ @@ -199,10 +200,51 @@ typedef struct wasmtime_component_valrecord_entry { wasmtime_component_val_t val; } wasmtime_component_valrecord_entry_t; -/// \brief Allocates a new #wasmtime_component_val_t -WASM_API_EXTERN wasmtime_component_val_t *wasmtime_component_val_new(); +/// \brief Allocates a new `wasmtime_component_val_t` on the heap, initializing +/// it with the contents of `val`. +/// +/// This function is intended to be used with the `variant`, `result`, and +/// `option` fields of `wasmtime_component_valunion_t`. The returned pointer +/// must be deallocated with `wasmtime_component_val_free` to deallocate the +/// heap-owned pointer. The `val` provided is "taken" meaning that its contents +/// are transferred to the returned pointer. This is not a clone operation but +/// instead is an operation used to move `val` onto a Wasmtime-defined heap +/// allocation. +/// +/// Note that `wasmtime_component_val_delete` should not be used for +/// deallocating the return value because that will deallocate the contents of +/// the value but not the value pointer itself. +WASM_API_EXTERN wasmtime_component_val_t * +wasmtime_component_val_new(wasmtime_component_val_t *val); -/// \brief Calls the destructor on \p value deallocating any owned memory +/// \brief Deallocates the heap-allocated value at `ptr`. +/// +/// This function will perform `wasmtime_component_val_delete` on `ptr` and +/// then it will deallocate `ptr` itself. This should not be used on +/// embedder-owned `ptr` storage. This function is used to clean up +/// allocations made by `wasmtime_component_val_new`. +WASM_API_EXTERN void wasmtime_component_val_free(wasmtime_component_val_t *ptr); + +/// \brief Performs a deep copy of the provided `src`, storing the results into +/// `dst`. +/// +/// The `dst` value must have `wasmtime_component_val_delete` run to discard +/// its contents. +WASM_API_EXTERN void +wasmtime_component_val_clone(const wasmtime_component_val_t *src, + wasmtime_component_val_t *dst); + +/// \brief Deallocates any memory owned by `value`. +/// +/// This function will look at `value->kind` and deallocate any memory if +/// necessary. For example lists will deallocate +/// `value->of.list`. +/// +/// Note that this function is not to be confused with +/// `wasmtime_component_val_free` which not only deallocates the memory that +/// `value` owns but also deallocates the memory of `value` itself. This +/// function should only be used when the embedder owns the pointer `value` +/// itself. WASM_API_EXTERN void wasmtime_component_val_delete(wasmtime_component_val_t *value); diff --git a/crates/c-api/include/wasmtime/component/val.hh b/crates/c-api/include/wasmtime/component/val.hh new file mode 100644 index 000000000000..0c29bfbb0331 --- /dev/null +++ b/crates/c-api/include/wasmtime/component/val.hh @@ -0,0 +1,823 @@ +/// \file wasmtime/component/val.hh + +#ifndef WASMTIME_COMPONENT_VAL_HH +#define WASMTIME_COMPONENT_VAL_HH + +#include + +#ifdef WASMTIME_FEATURE_COMPONENT_MODEL + +#include +#include +#include +#include +#include +#include + +namespace wasmtime { +namespace component { + +class Val; +class Record; + +namespace detail { + +/// Internal helper to convert from C to `Val`, sort of a forward-declaration +/// of `Val::from_capi` which I don't know how to otherwise forward-declare. +inline const Val *val_from_capi(const wasmtime_component_val_t *capi) { + return reinterpret_cast(capi); +} + +} // namespace detail + +/// Internal helper macro to define ownership-semanitcs for C++ types based on +/// a C type as a single member where operations are defined in terms of +/// `transfer`, `copy`, and `destroy` functions. +#define VAL_REPR(name, raw_type) \ +private: \ + using Raw = raw_type; \ + Raw raw; \ + \ +public: \ + /** \ + * Create a variant that takes ownership of the underlying C API variant. \ + */ \ + explicit name(Raw &&capi) { name::transfer(std::move(capi), raw); } \ + \ + /** \ + * Converts the raw C API representation to this class without taking \ + * ownership. \ + */ \ + static const name *from_capi(const Raw *capi) { \ + return reinterpret_cast(capi); \ + } \ + \ + /** \ + * Converts the raw C API representation to this class without taking \ + * ownership. \ + */ \ + static name *from_capi(Raw *capi) { return reinterpret_cast(capi); } \ + \ + /** \ + * Converts to the raw C API representation to this class without taking \ + * ownership. \ + */ \ + static const Raw *to_capi(const name *capi) { \ + return reinterpret_cast(capi); \ + } \ + \ + /** \ + * Converts to the raw C API representation to this class without taking \ + * ownership. \ + */ \ + static Raw *to_capi(name *capi) { return reinterpret_cast(capi); } \ + \ + /** \ + * \brief Copy constructor to clone `other`. \ + */ \ + name(const name &other) { copy(other.raw); } \ + \ + /** \ + * \brief Copy assignment to clone from `other`. \ + */ \ + name &operator=(const name &other) { \ + destroy(); \ + copy(other.raw); \ + return *this; \ + } \ + \ + /** \ + * \brief Move constructor to move the contents of `other`. \ + */ \ + name(name &&other) { name::transfer(std::move(other.raw), raw); } \ + \ + /** \ + * \brief Move assignment to move the contents of `other`. \ + */ \ + name &operator=(name &&other) { \ + destroy(); \ + name::transfer(std::move(other.raw), raw); \ + return *this; \ + } \ + \ + ~name() { destroy(); } \ + \ + /** \ + * \brief Returns a pointer to the underlying C API representation. \ + */ \ + const Raw *capi() const { return &raw; } \ + \ +private: + +/// \brief Class representing a field in a record value. +class RecordField { + friend class Record; + + wasmtime_component_valrecord_entry_t entry; + + // This value can't be constructed or destructed, it's only used in iteration + // of `Record`. + RecordField() = delete; + ~RecordField() = delete; + + static const RecordField * + from_capi(const wasmtime_component_valrecord_entry_t *capi) { + return reinterpret_cast(capi); + } + +public: + /// \brief Returns the name of this record field. + std::string_view name() const { + return std::string_view{entry.name.data, entry.name.size}; + } + + /// \brief Returns the value of this record field. + const Val &value() const { return *detail::val_from_capi(&entry.val); } +}; + +/// \brief Class representing a component model record, a list of name/value +/// pairs. +class Record { + friend class Val; + + VAL_REPR(Record, wasmtime_component_valrecord_t); + + static void transfer(Raw &&from, Raw &to) { + to = from; + from.size = 0; + from.data = nullptr; + } + + void copy(const Raw &other) { + wasmtime_component_valrecord_copy(&raw, &other); + } + + void destroy() { wasmtime_component_valrecord_delete(&raw); } + +public: + /// Creates a new record from the named field pairs provided. + Record(std::vector> entries); + + /// \brief Returns the number of entries in the record. + size_t size() const { return raw.size; } + + /// \brief Returns an iterator to the beginning of the record fields. + const RecordField *begin() const { return RecordField::from_capi(raw.data); } + + /// \brief Returns an iterator to the end of the record fields. + const RecordField *end() const { + return RecordField::from_capi(raw.data + raw.size); + } +}; + +/// \brief Class representing a component model list, a sequence of values. +class List { + friend class Val; + + VAL_REPR(List, wasmtime_component_vallist_t); + + static void transfer(Raw &&from, Raw &to) { + to = from; + from.size = 0; + from.data = nullptr; + } + + void copy(const Raw &other) { wasmtime_component_vallist_copy(&raw, &other); } + + void destroy() { wasmtime_component_vallist_delete(&raw); } + +public: + /// Creates a new list from the named field pairs provided. + List(std::vector entries); + + /// \brief Returns the number of entries in the list. + size_t size() const { return raw.size; } + + /// \brief Returns an iterator to the beginning of the list. + const Val *begin() const { return reinterpret_cast(raw.data); } + + /// \brief Returns an iterator to the end of the list. + const Val *end() const { + return reinterpret_cast(raw.data + raw.size); + } +}; + +/// \brief Class representing a component model tuple. +class Tuple { + friend class Val; + + VAL_REPR(Tuple, wasmtime_component_valtuple_t); + + static void transfer(Raw &&from, Raw &to) { + to = from; + from.size = 0; + from.data = nullptr; + } + + void copy(const Raw &other) { + wasmtime_component_valtuple_copy(&raw, &other); + } + + void destroy() { wasmtime_component_valtuple_delete(&raw); } + +public: + /// Creates a new tuple from the named field pairs provided. + Tuple(std::vector entries); + + /// \brief Returns the number of entries in the tuple. + size_t size() const { return raw.size; } + + /// \brief Returns an iterator to the beginning of the tuple. + const Val *begin() const { return reinterpret_cast(raw.data); } + + /// \brief Returns an iterator to the end of the tuple. + const Val *end() const { + return reinterpret_cast(raw.data + raw.size); + } +}; + +/// Class representing a component model `variant` value. +class Variant { + friend class Val; + + VAL_REPR(Variant, wasmtime_component_valvariant_t); + + static void transfer(wasmtime_component_valvariant_t &&from, + wasmtime_component_valvariant_t &to) { + to = from; + from.discriminant.size = 0; + from.discriminant.data = nullptr; + from.val = nullptr; + } + + void copy(const wasmtime_component_valvariant_t &other) { + wasm_name_copy(&raw.discriminant, &other.discriminant); + if (other.val) { + wasmtime_component_val_t clone; + wasmtime_component_val_clone(other.val, &clone); + raw.val = wasmtime_component_val_new(&clone); + } else { + raw.val = nullptr; + } + } + + void destroy() { + wasm_name_delete(&raw.discriminant); + wasmtime_component_val_free(raw.val); + } + +public: + /// Constructs a new variant value with the provided discriminant and payload. + Variant(std::string_view discriminant, std::optional x); + + /// Returns the name of the discriminant of this value. + std::string_view discriminant() const { + return std::string_view(raw.discriminant.data, raw.discriminant.size); + } + + /// Returns the optional payload value associated with this variant value. + const Val *value() const { return detail::val_from_capi(raw.val); } +}; + +/// Class representing a component model `option` value. +class WitOption { + friend class Val; + + VAL_REPR(WitOption, wasmtime_component_val_t *); + + static void transfer(Raw &&from, Raw &to) { + to = from; + from = nullptr; + } + + void copy(const Raw &other) { + if (other) { + wasmtime_component_val_t clone; + wasmtime_component_val_clone(other, &clone); + raw = wasmtime_component_val_new(&clone); + } else { + raw = nullptr; + } + } + + void destroy() { wasmtime_component_val_free(raw); } + +public: + /// Constructs a new option value with the provided value. + explicit WitOption(std::optional val); + + /// Returns the optional payload value associated with this option. + const Val *value() const { return detail::val_from_capi(raw); } +}; + +/// Class representing a component model `result` value. +class WitResult { + friend class Val; + + VAL_REPR(WitResult, wasmtime_component_valresult_t); + + static void transfer(Raw &&from, Raw &to) { + to = from; + from.val = nullptr; + } + + void copy(const Raw &other) { + raw.is_ok = other.is_ok; + if (other.val) { + wasmtime_component_val_t clone; + wasmtime_component_val_clone(other.val, &clone); + raw.val = wasmtime_component_val_new(&clone); + } else { + raw.val = nullptr; + } + } + + void destroy() { + if (raw.val) + wasmtime_component_val_free(raw.val); + } + +public: + /// Constructs a new result value with the `ok` variant. + static WitResult ok(std::optional val); + + /// Constructs a new result value with the `err` variant. + static WitResult err(std::optional val); + + /// \brief Returns whether this result is the `ok` variant. + bool is_ok() const { return raw.is_ok; } + + /// Returns the optional payload value associated with this result. + const Val *payload() const { return detail::val_from_capi(raw.val); } +}; + +/// Class representing a component model `flags` value. +class Flag { + friend class Flags; + + VAL_REPR(Flag, wasm_name_t); + + static void transfer(Raw &&from, Raw &to) { + to = from; + from.size = 0; + from.data = nullptr; + } + + void copy(const Raw &other) { wasm_name_copy(&raw, &other); } + + void destroy() { wasm_name_delete(&raw); } + +public: + /// Creates a new flag from the provided string. + Flag(std::string_view name) { wasm_name_new(&raw, name.size(), name.data()); } + + /// \brief Returns the name of this flag. + std::string_view name() const { return std::string_view{raw.data, raw.size}; } +}; + +/// Class representing a component model `flags` value. +class Flags { + friend class Val; + + VAL_REPR(Flags, wasmtime_component_valflags_t); + + static void transfer(Raw &&from, Raw &to) { + to = from; + from.size = 0; + from.data = nullptr; + } + + void copy(const Raw &other) { + wasmtime_component_valflags_copy(&raw, &other); + } + + void destroy() { wasmtime_component_valflags_delete(&raw); } + +public: + /// Creates a new flags value from the provided flags. + Flags(std::vector flags) { + wasmtime_component_valflags_new_uninit(&raw, flags.size()); + auto dst = raw.data; + for (auto &&val : flags) + Flag::transfer(std::move(val.raw), *dst++); + } + + /// \brief Returns the number of flags. + size_t size() const { return raw.size; } + + /// \brief Returns an iterator to the beginning of the flags. + const Flag *begin() const { return reinterpret_cast(raw.data); } + + /// \brief Returns an iterator to the end of the flags. + const Flag *end() const { + return reinterpret_cast(raw.data + raw.size); + } +}; + +/** + * \brief Class representing an instantiated WebAssembly component. + */ +class Val { + friend class Variant; + friend class WitOption; + friend class WitResult; + + VAL_REPR(Val, wasmtime_component_val_t); + + static void transfer(Raw &&from, Raw &to) { + to = from; + from.kind = WASMTIME_COMPONENT_BOOL; + from.of.boolean = false; + } + + void copy(const Raw &other) { wasmtime_component_val_clone(&other, &raw); } + + void destroy() { wasmtime_component_val_delete(&raw); } + +public: + /// Creates a new boolean value. + Val(bool v) { + raw.kind = WASMTIME_COMPONENT_BOOL; + raw.of.boolean = v; + } + + /// Creates a new u8 value. + Val(uint8_t v) { + raw.kind = WASMTIME_COMPONENT_U8; + raw.of.u8 = v; + } + + /// Creates a new s8 value. + Val(int8_t v) { + raw.kind = WASMTIME_COMPONENT_S8; + raw.of.s8 = v; + } + + /// Creates a new u16 value. + Val(uint16_t v) { + raw.kind = WASMTIME_COMPONENT_U16; + raw.of.u16 = v; + } + + /// Creates a new s16 value. + Val(int16_t v) { + raw.kind = WASMTIME_COMPONENT_S16; + raw.of.s16 = v; + } + + /// Creates a new u32 value. + Val(uint32_t v) { + raw.kind = WASMTIME_COMPONENT_U32; + raw.of.u32 = v; + } + + /// Creates a new s32 value. + Val(int32_t v) { + raw.kind = WASMTIME_COMPONENT_S32; + raw.of.s32 = v; + } + + /// Creates a new u64 value. + Val(uint64_t v) { + raw.kind = WASMTIME_COMPONENT_U64; + raw.of.u64 = v; + } + + /// Creates a new s64 value. + Val(int64_t v) { + raw.kind = WASMTIME_COMPONENT_S64; + raw.of.s64 = v; + } + + /// Creates a new f32 value. + Val(float v) { + raw.kind = WASMTIME_COMPONENT_F32; + raw.of.f32 = v; + } + + /// Creates a new f64 value. + Val(double v) { + raw.kind = WASMTIME_COMPONENT_F64; + raw.of.f64 = v; + } + + /// Creates a new char value. + static Val char_(uint32_t v) { + wasmtime_component_val_t raw = { + .kind = WASMTIME_COMPONENT_CHAR, + .of = {.character = v}, + }; + return Val(std::move(raw)); + } + + /// Creates a new string value. + static Val string(std::string_view v) { + wasmtime_component_val_t raw; + raw.kind = WASMTIME_COMPONENT_STRING; + wasm_byte_vec_new(&raw.of.string, v.size(), v.data()); + return Val(std::move(raw)); + } + + /// Creates a new list value. + Val(List v) { + raw.kind = WASMTIME_COMPONENT_LIST; + List::transfer(std::move(v.raw), raw.of.list); + } + + /// Creates a new record value. + Val(Record r) { + raw.kind = WASMTIME_COMPONENT_RECORD; + Record::transfer(std::move(r.raw), raw.of.record); + } + + /// Creates a new tuple value. + Val(Tuple v) { + raw.kind = WASMTIME_COMPONENT_TUPLE; + Tuple::transfer(std::move(v.raw), raw.of.tuple); + } + + /// Creates a new variant value. + Val(Variant v) { + raw.kind = WASMTIME_COMPONENT_VARIANT; + Variant::transfer(std::move(v.raw), raw.of.variant); + } + + /// Creates a new option value. + Val(WitOption v) { + raw.kind = WASMTIME_COMPONENT_OPTION; + WitOption::transfer(std::move(v.raw), raw.of.option); + } + + /// Creates a new result value. + Val(WitResult r) { + raw.kind = WASMTIME_COMPONENT_RESULT; + WitResult::transfer(std::move(r.raw), raw.of.result); + } + + /// Creates a new enum value. + static Val enum_(std::string_view discriminant) { + wasmtime_component_val_t raw; + raw.kind = WASMTIME_COMPONENT_ENUM; + wasm_byte_vec_new(&raw.of.enumeration, discriminant.size(), + discriminant.data()); + return Val(std::move(raw)); + } + + /// Creates a new flags value. + Val(Flags f) { + raw.kind = WASMTIME_COMPONENT_FLAGS; + Flags::transfer(std::move(f.raw), raw.of.flags); + } + + /// \brief Returns whether this value is a boolean. + bool is_bool() const { return raw.kind == WASMTIME_COMPONENT_BOOL; } + + /// \brief Returns the boolean value, only valid if `is_bool()`. + bool get_bool() const { + assert(is_bool()); + return raw.of.boolean; + } + + /// \brief Returns whether this value is a u8. + bool is_u8() const { return raw.kind == WASMTIME_COMPONENT_U8; } + + /// \brief Returns the u8 value, only valid if `is_u8()`. + uint8_t get_u8() const { + assert(is_u8()); + return raw.of.u8; + } + + /// \brief Returns whether this value is a s8. + bool is_s8() const { return raw.kind == WASMTIME_COMPONENT_S8; } + + /// \brief Returns the s8 value, only valid if `is_s8()`. + int8_t get_s8() const { + assert(is_s8()); + return raw.of.s8; + } + + /// \brief Returns whether this value is a u16. + bool is_u16() const { return raw.kind == WASMTIME_COMPONENT_U16; } + + /// \brief Returns the u16 value, only valid if `is_u16()`. + uint16_t get_u16() const { + assert(is_u16()); + return raw.of.u16; + } + + /// \brief Returns whether this value is a s16. + bool is_s16() const { return raw.kind == WASMTIME_COMPONENT_S16; } + + /// \brief Returns the s16 value, only valid if `is_s16()`. + int16_t get_s16() const { + assert(is_s16()); + return raw.of.s16; + } + + /// \brief Returns whether this value is a u32. + bool is_u32() const { return raw.kind == WASMTIME_COMPONENT_U32; } + + /// \brief Returns the u32 value, only valid if `is_u32()`. + uint32_t get_u32() const { + assert(is_u32()); + return raw.of.u32; + } + + /// \brief Returns whether this value is a s32. + bool is_s32() const { return raw.kind == WASMTIME_COMPONENT_S32; } + + /// \brief Returns the s32 value, only valid if `is_s32()`. + int32_t get_s32() const { + assert(is_s32()); + return raw.of.s32; + } + + /// \brief Returns whether this value is a u64. + bool is_u64() const { return raw.kind == WASMTIME_COMPONENT_U64; } + + /// \brief Returns the u64 value, only valid if `is_u64()`. + uint64_t get_u64() const { + assert(is_u64()); + return raw.of.u64; + } + + /// \brief Returns whether this value is a s64. + bool is_s64() const { return raw.kind == WASMTIME_COMPONENT_S64; } + + /// \brief Returns the s64 value, only valid if `is_s64()`. + int64_t get_s64() const { + assert(is_s64()); + return raw.of.s64; + } + + /// \brief Returns whether this value is a f32. + bool is_f32() const { return raw.kind == WASMTIME_COMPONENT_F32; } + + /// \brief Returns the f32 value, only valid if `is_f32()`. + float get_f32() const { + assert(is_f32()); + return raw.of.f32; + } + + /// \brief Returns whether this value is a f64. + bool is_f64() const { return raw.kind == WASMTIME_COMPONENT_F64; } + + /// \brief Returns the f64 value, only valid if `is_f64()`. + double get_f64() const { + assert(is_f64()); + return raw.of.f64; + } + + /// \brief Returns whether this value is a string. + bool is_string() const { return raw.kind == WASMTIME_COMPONENT_STRING; } + + /// \brief Returns the string value, only valid if `is_string()`. + std::string_view get_string() const { + assert(is_string()); + return std::string_view(raw.of.string.data, raw.of.string.size); + } + + /// \brief Returns whether this value is a list. + bool is_list() const { return raw.kind == WASMTIME_COMPONENT_LIST; } + + /// \brief Returns the list value, only valid if `is_list()`. + const List &get_list() const { + assert(is_list()); + return *List::from_capi(&raw.of.list); + } + + /// \brief Returns whether this value is a record. + bool is_record() const { return raw.kind == WASMTIME_COMPONENT_RECORD; } + + /// \brief Returns the record value, only valid if `is_record()`. + const Record &get_record() const { + assert(is_record()); + return *Record::from_capi(&raw.of.record); + } + + /// \brief Returns whether this value is a tuple. + bool is_tuple() const { return raw.kind == WASMTIME_COMPONENT_TUPLE; } + + /// \brief Returns the tuple value, only valid if `is_tuple()`. + const Tuple &get_tuple() const { + assert(is_tuple()); + return *Tuple::from_capi(&raw.of.tuple); + } + + /// \brief Returns whether this value is a variant. + bool is_variant() const { return raw.kind == WASMTIME_COMPONENT_VARIANT; } + + /// \brief Returns the variant value, only valid if `is_variant()`. + const Variant &get_variant() const { + assert(is_variant()); + return *Variant::from_capi(&raw.of.variant); + } + + /// \brief Returns whether this value is an option. + bool is_option() const { return raw.kind == WASMTIME_COMPONENT_OPTION; } + + /// \brief Returns the option value, only valid if `is_option()`. + const WitOption &get_option() const { + assert(is_option()); + return *WitOption::from_capi(&raw.of.option); + } + + /// \brief Returns whether this value is an enum. + bool is_enum() const { return raw.kind == WASMTIME_COMPONENT_ENUM; } + + /// \brief Returns the enum discriminant, only valid if `is_enum()`. + std::string_view get_enum() const { + assert(is_enum()); + return std::string_view(raw.of.enumeration.data, raw.of.enumeration.size); + } + + /// \brief Returns whether this value is a result. + bool is_result() const { return raw.kind == WASMTIME_COMPONENT_RESULT; } + + /// \brief Returns the result value, only valid if `is_result()`. + const WitResult &get_result() const { + assert(is_result()); + return *WitResult::from_capi(&raw.of.result); + } + + /// \brief Returns whether this value is flags. + bool is_flags() const { return raw.kind == WASMTIME_COMPONENT_FLAGS; } + + /// \brief Returns the flags value, only valid if `is_flags()`. + const Flags &get_flags() const { + assert(is_flags()); + return *Flags::from_capi(&raw.of.flags); + } +}; + +#undef VAL_REPR + +inline Record::Record(std::vector> entries) { + wasmtime_component_valrecord_new_uninit(&raw, entries.size()); + auto dst = raw.data; + for (auto &&[name, val] : entries) { + wasm_byte_vec_new(&dst->name, name.size(), name.data()); + new (&dst->val) Val(std::move(val)); + dst++; + } +} + +inline List::List(std::vector values) { + wasmtime_component_vallist_new_uninit(&raw, values.size()); + auto dst = raw.data; + for (auto &&val : values) + new (dst++) Val(std::move(val)); +} + +inline Tuple::Tuple(std::vector values) { + wasmtime_component_valtuple_new_uninit(&raw, values.size()); + auto dst = raw.data; + for (auto &&val : values) + new (dst++) Val(std::move(val)); +} + +inline Variant::Variant(std::string_view discriminant, std::optional x) { + wasm_name_new(&raw.discriminant, discriminant.size(), discriminant.data()); + if (x) { + raw.val = wasmtime_component_val_new(&x->raw); + } else { + raw.val = nullptr; + } +} + +inline WitOption::WitOption(std::optional v) { + if (v) { + raw = wasmtime_component_val_new(&v->raw); + } else { + raw = nullptr; + } +} + +inline WitResult WitResult::ok(std::optional v) { + wasmtime_component_valresult_t raw; + raw.is_ok = true; + if (v) { + raw.val = wasmtime_component_val_new(&v->raw); + } else { + raw.val = nullptr; + } + return WitResult(std::move(raw)); +} + +inline WitResult WitResult::err(std::optional v) { + wasmtime_component_valresult_t raw; + raw.is_ok = false; + if (v) { + raw.val = wasmtime_component_val_new(&v->raw); + } else { + raw.val = nullptr; + } + return WitResult(std::move(raw)); +} + +} // namespace component +} // namespace wasmtime + +#endif // WASMTIME_FEATURE_COMPONENT_MODEL + +#endif // WASMTIME_COMPONENT_VAL_H diff --git a/crates/c-api/include/wasmtime/linker.h b/crates/c-api/include/wasmtime/linker.h index f3222d505a8d..6d64640e1c0e 100644 --- a/crates/c-api/include/wasmtime/linker.h +++ b/crates/c-api/include/wasmtime/linker.h @@ -68,6 +68,24 @@ WASM_API_EXTERN void wasmtime_linker_delete(wasmtime_linker_t *linker); WASM_API_EXTERN void wasmtime_linker_allow_shadowing(wasmtime_linker_t *linker, bool allow_shadowing); +/** + * \brief Ensures that `module` can be instantiated with this linker by defining + * all functions otherwise missing from this linker to trap. + */ +WASM_API_EXTERN wasmtime_error_t * +wasmtime_linker_define_unknown_imports_as_traps(wasmtime_linker_t *linker, + wasmtime_module_t *module); + +/** + * \brief Ensures that `module` can be instantiated with this linker by defining + * all functions otherwise missing from this linker to return the 'default' for + * what that import is. + */ +WASM_API_EXTERN wasmtime_error_t * +wasmtime_linker_define_unknown_imports_as_default_values( + wasmtime_linker_t *linker, wasmtime_context_t *store, + wasmtime_module_t *module); + /** * \brief Defines a new item in this linker. * diff --git a/crates/c-api/include/wasmtime/linker.hh b/crates/c-api/include/wasmtime/linker.hh index fbc943435799..afd44feebba0 100644 --- a/crates/c-api/include/wasmtime/linker.hh +++ b/crates/c-api/include/wasmtime/linker.hh @@ -180,6 +180,30 @@ public: } return Func(item); } + + /// \brief Defines any import of `module` previously unknown to this linker + /// as a trap. + Result define_unknown_imports_as_traps(Module &module) { + auto *error = wasmtime_linker_define_unknown_imports_as_traps( + ptr.get(), module.ptr.get()); + if (error != nullptr) { + return Error(error); + } + return std::monostate(); + } + + /// \brief Defines any import of `module` previously unknown to this linker + /// as the "default" value for that import, for example a function that + /// returns zeros. + Result + define_unknown_imports_as_default_values(Store::Context cx, Module &module) { + auto *error = wasmtime_linker_define_unknown_imports_as_default_values( + ptr.get(), cx.ptr, module.ptr.get()); + if (error != nullptr) { + return Error(error); + } + return std::monostate(); + } }; } // namespace wasmtime diff --git a/crates/c-api/include/wasmtime/store.h b/crates/c-api/include/wasmtime/store.h index 687497006923..65613041c93c 100644 --- a/crates/c-api/include/wasmtime/store.h +++ b/crates/c-api/include/wasmtime/store.h @@ -11,7 +11,6 @@ #include #include #include -#include #ifdef __cplusplus extern "C" { @@ -193,22 +192,6 @@ wasmtime_context_get_fuel(const wasmtime_context_t *context, uint64_t *fuel); WASM_API_EXTERN wasmtime_error_t * wasmtime_context_set_wasi(wasmtime_context_t *context, wasi_config_t *wasi); -#ifdef WASMTIME_FEATURE_COMPONENT_MODEL - -/** - * \brief Set the WASIP2 config for this store. - * - * This function is required if #wasmtime_component_linker_add_wasip2 is called. - * - * This function takes ownership of \p config. The caller should no longer use - * \p config after calling this function. - */ -WASM_API_EXTERN void -wasmtime_context_set_wasip2(wasmtime_context_t *context, - wasmtime_wasip2_config_t *config); - -#endif // WASMTIME_FEATURE_COMPONENT_MODEL - #endif // WASMTIME_FEATURE_WASI /** diff --git a/crates/c-api/include/wasmtime/store.hh b/crates/c-api/include/wasmtime/store.hh index a0830a5ffbd7..fe45b80d9261 100644 --- a/crates/c-api/include/wasmtime/store.hh +++ b/crates/c-api/include/wasmtime/store.hh @@ -70,9 +70,10 @@ public: friend class Store; wasmtime_context_t *ptr; - Context(wasmtime_context_t *ptr) : ptr(ptr) {} - public: + /// Creates a context from the raw C API pointer. + explicit Context(wasmtime_context_t *ptr) : ptr(ptr) {} + /// Creates a context referencing the provided `Store`. Context(Store &store) : Context(wasmtime_store_context(store.ptr.get())) {} /// Creates a context referencing the provided `Store`. diff --git a/crates/c-api/include/wasmtime/wasip2.h b/crates/c-api/include/wasmtime/wasip2.h deleted file mode 100644 index 9b02e3a2d992..000000000000 --- a/crates/c-api/include/wasmtime/wasip2.h +++ /dev/null @@ -1,72 +0,0 @@ -/// \file wasmtime/wasip2.h - -#ifndef WASMTIME_WASIP2_H -#define WASMTIME_WASIP2_H - -#include -#include - -#ifdef WASMTIME_FEATURE_WASI -#ifdef WASMTIME_FEATURE_COMPONENT_MODEL - -#ifdef __cplusplus -extern "C" { -#endif - -/// Config for the WASIP2 context. -typedef struct wasmtime_wasip2_config_t wasmtime_wasip2_config_t; - -/** - * \brief Create a #wasmtime_wasip2_config_t - */ -WASM_API_EXTERN wasmtime_wasip2_config_t *wasmtime_wasip2_config_new(); - -/** - * \brief Configures this context's stdin stream to read the host process's - * stdin. - * - * Note that concurrent reads of stdin can produce surprising results so when - * using this it's typically best to have a single wasm instance in the process - * using this. - */ -WASM_API_EXTERN void -wasmtime_wasip2_config_inherit_stdin(wasmtime_wasip2_config_t *config); - -/** - * \brief Configures this context's stdout stream to write to the host process's - * stdout. - */ -WASM_API_EXTERN void -wasmtime_wasip2_config_inherit_stdout(wasmtime_wasip2_config_t *config); - -/** - * \brief Configures this context's stderr stream to write to the host process's - * stderr. - */ -WASM_API_EXTERN void -wasmtime_wasip2_config_inherit_stderr(wasmtime_wasip2_config_t *config); - -/** - * \brief Appends a single argument to get passed to wasm. - */ -WASM_API_EXTERN void -wasmtime_wasip2_config_arg(wasmtime_wasip2_config_t *config, const char *arg, - size_t arg_len); - -/** - * \brief Delete a #wasmtime_wasip2_config_t - * - * \note This is not needed if the config is passed to - * #wasmtime_component_linker_add_wasip2 - */ -WASM_API_EXTERN void -wasmtime_wasip2_config_delete(wasmtime_wasip2_config_t *config); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // WASMTIME_FEATURE_COMPONENT_MODEL -#endif // WASMTIME_FEATURE_WASI - -#endif // WASMTIME_WASIP2_H diff --git a/crates/c-api/src/component/linker.rs b/crates/c-api/src/component/linker.rs index 05797dbfe14e..f005448112e4 100644 --- a/crates/c-api/src/component/linker.rs +++ b/crates/c-api/src/component/linker.rs @@ -27,6 +27,14 @@ pub unsafe extern "C" fn wasmtime_component_linker_new( }) } +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_component_linker_allow_shadowing( + linker: &mut wasmtime_component_linker_t, + allow: bool, +) { + linker.linker.allow_shadowing(allow); +} + #[unsafe(no_mangle)] pub unsafe extern "C" fn wasmtime_component_linker_root( linker: &mut wasmtime_component_linker_t, diff --git a/crates/c-api/src/component/val.rs b/crates/c-api/src/component/val.rs index c11a7c7b65d5..dfedf5dc332c 100644 --- a/crates/c-api/src/component/val.rs +++ b/crates/c-api/src/component/val.rs @@ -3,7 +3,7 @@ use wasmtime::component::Val; use crate::wasm_name_t; use std::mem; -use std::mem::MaybeUninit; +use std::mem::{ManuallyDrop, MaybeUninit}; use std::ptr; use std::slice; @@ -315,13 +315,28 @@ impl From<&Val> for wasmtime_component_val_t { } #[unsafe(no_mangle)] -pub unsafe extern "C" fn wasmtime_component_val_new() -> Box { - Box::new(wasmtime_component_val_t::default()) +pub unsafe extern "C" fn wasmtime_component_val_new( + src: &mut wasmtime_component_val_t, +) -> Box { + Box::new(mem::replace(src, wasmtime_component_val_t::default())) } #[unsafe(no_mangle)] -pub unsafe extern "C" fn wasmtime_component_val_delete(value: *mut wasmtime_component_val_t) { +pub unsafe extern "C" fn wasmtime_component_val_free(_dst: Option>) {} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_component_val_clone( + src: &wasmtime_component_val_t, + dst: &mut MaybeUninit, +) { + dst.write(src.clone()); +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_component_val_delete( + value: &mut ManuallyDrop, +) { unsafe { - std::ptr::drop_in_place(value); + ManuallyDrop::drop(value); } } diff --git a/crates/c-api/src/lib.rs b/crates/c-api/src/lib.rs index 31b1cc74e7d4..5d5abad2a29d 100644 --- a/crates/c-api/src/lib.rs +++ b/crates/c-api/src/lib.rs @@ -66,11 +66,6 @@ mod wasi; #[cfg(feature = "wasi")] pub use crate::wasi::*; -#[cfg(all(feature = "component-model", feature = "wasi"))] -mod wasip2; -#[cfg(all(feature = "component-model", feature = "wasi"))] -pub use crate::wasip2::*; - #[cfg(feature = "wat")] mod wat2wasm; #[cfg(feature = "wat")] diff --git a/crates/c-api/src/linker.rs b/crates/c-api/src/linker.rs index 0dc7ae8c5fc3..93a0d38631d1 100644 --- a/crates/c-api/src/linker.rs +++ b/crates/c-api/src/linker.rs @@ -211,3 +211,30 @@ pub unsafe extern "C" fn wasmtime_linker_get( None => false, } } + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_linker_define_unknown_imports_as_traps( + linker: &mut wasmtime_linker_t, + module: &wasmtime_module_t, +) -> Option> { + handle_result( + linker + .linker + .define_unknown_imports_as_traps(&module.module), + |_| (), + ) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_linker_define_unknown_imports_as_default_values( + linker: &mut wasmtime_linker_t, + store: WasmtimeStoreContextMut<'_>, + module: &wasmtime_module_t, +) -> Option> { + handle_result( + linker + .linker + .define_unknown_imports_as_default_values(store, &module.module), + |_| (), + ) +} diff --git a/crates/c-api/src/store.rs b/crates/c-api/src/store.rs index 6381e1b24337..32531eb1578c 100644 --- a/crates/c-api/src/store.rs +++ b/crates/c-api/src/store.rs @@ -95,21 +95,12 @@ pub struct WasmtimeStoreData { /// Limits for the store. pub store_limits: StoreLimits, - - #[cfg(feature = "component-model")] - pub(crate) resource_table: wasmtime::component::ResourceTable, - - #[cfg(all(feature = "component-model", feature = "wasi"))] - pub(crate) wasip2: Option, } #[cfg(all(feature = "component-model", feature = "wasi"))] impl wasmtime_wasi::WasiView for WasmtimeStoreData { fn ctx(&mut self) -> wasmtime_wasi::WasiCtxView<'_> { - wasmtime_wasi::WasiCtxView { - ctx: self.wasip2.as_mut().unwrap(), - table: &mut self.resource_table, - } + self.wasi.as_mut().unwrap().ctx() } } @@ -129,10 +120,6 @@ pub extern "C" fn wasmtime_store_new( hostcall_val_storage: Vec::new(), wasm_val_storage: Vec::new(), store_limits: StoreLimits::default(), - #[cfg(feature = "component-model")] - resource_table: wasmtime::component::ResourceTable::default(), - #[cfg(all(feature = "component-model", feature = "wasi"))] - wasip2: None, }, ), }) @@ -237,15 +224,6 @@ pub extern "C" fn wasmtime_context_set_wasi( }) } -#[cfg(all(feature = "component-model", feature = "wasi"))] -#[unsafe(no_mangle)] -pub unsafe extern "C" fn wasmtime_context_set_wasip2( - mut context: WasmtimeStoreContextMut<'_>, - mut config: Box, -) { - context.data_mut().wasip2 = Some(config.builder.build()); -} - #[unsafe(no_mangle)] pub extern "C" fn wasmtime_context_gc(mut context: WasmtimeStoreContextMut<'_>) { context.gc(None); diff --git a/crates/c-api/src/wasip2.rs b/crates/c-api/src/wasip2.rs deleted file mode 100644 index 0806810e2004..000000000000 --- a/crates/c-api/src/wasip2.rs +++ /dev/null @@ -1,46 +0,0 @@ -#[repr(transparent)] -pub struct wasmtime_wasip2_config_t { - pub(crate) builder: wasmtime_wasi::WasiCtxBuilder, -} - -#[unsafe(no_mangle)] -pub unsafe extern "C" fn wasmtime_wasip2_config_new() -> Box { - Box::new(wasmtime_wasip2_config_t { - builder: wasmtime_wasi::WasiCtxBuilder::new(), - }) -} - -#[unsafe(no_mangle)] -pub unsafe extern "C" fn wasmtime_wasip2_config_inherit_stdin( - config: &mut wasmtime_wasip2_config_t, -) { - config.builder.inherit_stdin(); -} - -#[unsafe(no_mangle)] -pub unsafe extern "C" fn wasmtime_wasip2_config_inherit_stdout( - config: &mut wasmtime_wasip2_config_t, -) { - config.builder.inherit_stdout(); -} - -#[unsafe(no_mangle)] -pub unsafe extern "C" fn wasmtime_wasip2_config_inherit_stderr( - config: &mut wasmtime_wasip2_config_t, -) { - config.builder.inherit_stderr(); -} - -#[unsafe(no_mangle)] -pub unsafe extern "C" fn wasmtime_wasip2_config_arg( - config: &mut wasmtime_wasip2_config_t, - arg: *const u8, - arg_len: usize, -) { - let arg = unsafe { std::slice::from_raw_parts(arg, arg_len) }; - let arg = std::str::from_utf8(arg).expect("valid utf-8"); - config.builder.arg(arg); -} - -#[unsafe(no_mangle)] -pub unsafe extern "C" fn wasmtime_wasip2_config_delete(_: Box) {} diff --git a/crates/c-api/tests/CMakeLists.txt b/crates/c-api/tests/CMakeLists.txt index a5d06cac7d30..423ca3575245 100644 --- a/crates/c-api/tests/CMakeLists.txt +++ b/crates/c-api/tests/CMakeLists.txt @@ -36,6 +36,7 @@ add_capi_test(tests FILES component/lookup_func.cc component/call_func.cc component/values.cc + component/linker.cc error.cc config.cc wat.cc diff --git a/crates/c-api/tests/component/call_func.cc b/crates/c-api/tests/component/call_func.cc index c670fc615496..87ae64b96491 100644 --- a/crates/c-api/tests/component/call_func.cc +++ b/crates/c-api/tests/component/call_func.cc @@ -1,12 +1,8 @@ -#include "utils.h" - #include #include -#include -#include +#include #include -using namespace wasmtime; using namespace wasmtime::component; TEST(component, call_func) { @@ -27,47 +23,28 @@ TEST(component, call_func) { )END", }; - Engine engine; - Store store(engine); + wasmtime::Engine engine; + wasmtime::Store store(engine); auto context = store.context(); auto component = Component::compile(engine, component_text).unwrap(); auto f = *component.export_index(nullptr, "f"); - const auto linker = wasmtime_component_linker_new(engine.capi()); - - wasmtime_component_instance_t instance = {}; - auto err = wasmtime_component_linker_instantiate(linker, context.capi(), - component.capi(), &instance); - CHECK_ERR(err); + Linker linker(engine); - wasmtime_component_func_t func = {}; - const auto found = wasmtime_component_instance_get_func( - &instance, context.capi(), f.capi(), &func); - EXPECT_TRUE(found); + auto instance = linker.instantiate(context, component).unwrap(); + auto func = *instance.get_func(context, f); - auto params = std::array{ - wasmtime_component_val_t{ - .kind = WASMTIME_COMPONENT_U32, - .of = {.u32 = 34}, - }, - wasmtime_component_val_t{ - .kind = WASMTIME_COMPONENT_U32, - .of = {.u32 = 35}, - }, + auto params = std::array{ + uint32_t(34), + uint32_t(35), }; - auto results = std::array{}; - - err = wasmtime_component_func_call(&func, context.capi(), params.data(), - params.size(), results.data(), - results.size()); - CHECK_ERR(err); + auto results = std::array{false}; - err = wasmtime_component_func_post_return(&func, context.capi()); - CHECK_ERR(err); + func.call(context, params, results).unwrap(); - EXPECT_EQ(results[0].kind, WASMTIME_COMPONENT_U32); - EXPECT_EQ(results[0].of.u32, 69); + func.post_return(context).unwrap(); - wasmtime_component_linker_delete(linker); + EXPECT_TRUE(results[0].is_u32()); + EXPECT_EQ(results[0].get_u32(), 69); } diff --git a/crates/c-api/tests/component/define_module.cc b/crates/c-api/tests/component/define_module.cc index 1049f4e0f6df..e104eae08e01 100644 --- a/crates/c-api/tests/component/define_module.cc +++ b/crates/c-api/tests/component/define_module.cc @@ -1,12 +1,8 @@ -#include "utils.h" - #include -#include -#include +#include #include #include -using namespace wasmtime; using namespace wasmtime::component; TEST(component, define_module) { @@ -32,33 +28,20 @@ TEST(component, define_module) { )END", }; - Engine engine; - Module module = Module::compile(engine, module_wat).unwrap(); - Store store(engine); + wasmtime::Engine engine; + wasmtime::Module module = + wasmtime::Module::compile(engine, module_wat).unwrap(); + wasmtime::Store store(engine); auto context = store.context(); Component component = Component::compile(engine, component_text).unwrap(); + Linker linker(engine); - const auto linker = wasmtime_component_linker_new(engine.capi()); - - const auto root = wasmtime_component_linker_root(linker); - - wasmtime_component_linker_instance_t *x_y_z = nullptr; - auto err = wasmtime_component_linker_instance_add_instance( - root, "x:y/z", strlen("x:y/z"), &x_y_z); - CHECK_ERR(err); - - err = wasmtime_component_linker_instance_add_module( - x_y_z, "mod", strlen("mod"), module.capi()); - CHECK_ERR(err); - - wasmtime_component_linker_instance_delete(x_y_z); - wasmtime_component_linker_instance_delete(root); - - wasmtime_component_instance_t instance = {}; - err = wasmtime_component_linker_instantiate(linker, context.capi(), - component.capi(), &instance); - CHECK_ERR(err); + { + auto root = linker.root(); + auto xyz = root.add_instance("x:y/z").unwrap(); + xyz.add_module("mod", module).unwrap(); + } - wasmtime_component_linker_delete(linker); + linker.instantiate(context, component).unwrap(); } diff --git a/crates/c-api/tests/component/instantiate.cc b/crates/c-api/tests/component/instantiate.cc index c3a8ada55e90..c6d9bc50d3aa 100644 --- a/crates/c-api/tests/component/instantiate.cc +++ b/crates/c-api/tests/component/instantiate.cc @@ -1,11 +1,7 @@ -#include "utils.h" - #include -#include -#include +#include #include -using namespace wasmtime; using namespace wasmtime::component; TEST(component, instantiate) { @@ -17,19 +13,11 @@ TEST(component, instantiate) { )END", }; - Engine engine; - Store store(engine); + wasmtime::Engine engine; + wasmtime::Store store(engine); auto context = store.context(); Component component = Component::compile(engine, bytes).unwrap(); + Linker linker(engine); - const auto linker = wasmtime_component_linker_new(engine.capi()); - EXPECT_NE(linker, nullptr); - - wasmtime_component_instance_t instance = {}; - auto error = wasmtime_component_linker_instantiate( - linker, context.capi(), component.capi(), &instance); - - CHECK_ERR(error); - - wasmtime_component_linker_delete(linker); + linker.instantiate(context, component).unwrap(); } diff --git a/crates/c-api/tests/component/linker.cc b/crates/c-api/tests/component/linker.cc new file mode 100644 index 000000000000..b3b0dc93450d --- /dev/null +++ b/crates/c-api/tests/component/linker.cc @@ -0,0 +1,16 @@ +#include +#include +#include + +using namespace wasmtime::component; + +TEST(Linker, allow_shadowing) { + wasmtime::Engine engine; + Linker linker(engine); + auto m = wasmtime::Module::compile(engine, "(module)").unwrap(); + + linker.root().add_module("x", m).unwrap(); + linker.root().add_module("x", m).err(); + linker.allow_shadowing(true); + linker.root().add_module("x", m).unwrap(); +} diff --git a/crates/c-api/tests/component/lookup_func.cc b/crates/c-api/tests/component/lookup_func.cc index 250903d9c0b9..310e3d6d9ed9 100644 --- a/crates/c-api/tests/component/lookup_func.cc +++ b/crates/c-api/tests/component/lookup_func.cc @@ -1,11 +1,7 @@ -#include "utils.h" - #include -#include -#include +#include #include -using namespace wasmtime; using namespace wasmtime::component; TEST(component, lookup_func) { @@ -22,8 +18,8 @@ TEST(component, lookup_func) { )END", }; - Engine engine; - Store store(engine); + wasmtime::Engine engine; + wasmtime::Store store(engine); auto context = store.context(); Component component = Component::compile(engine, component_text).unwrap(); auto f = component.export_index(nullptr, "ff"); @@ -34,23 +30,12 @@ TEST(component, lookup_func) { EXPECT_TRUE(f); - const auto linker = wasmtime_component_linker_new(engine.capi()); - - wasmtime_component_instance_t instance = {}; - auto err = wasmtime_component_linker_instantiate(linker, context.capi(), - component.capi(), &instance); - CHECK_ERR(err); + Linker linker(engine); - wasmtime_component_func_t func = {}; - const auto found = wasmtime_component_instance_get_func( - &instance, context.capi(), f->capi(), &func); - EXPECT_TRUE(found); - EXPECT_NE(func.store_id, 0); + auto instance = linker.instantiate(context, component).unwrap(); - auto f2 = wasmtime_component_instance_get_export_index( - &instance, context.capi(), nullptr, "f", strlen("f")); - EXPECT_NE(f2, nullptr); + *instance.get_func(context, *f); - wasmtime_component_export_index_delete(f2); - wasmtime_component_linker_delete(linker); + auto f2 = instance.get_export_index(context, nullptr, "f"); + EXPECT_TRUE(f2); } diff --git a/crates/c-api/tests/component/utils.h b/crates/c-api/tests/component/utils.h index 5405e69be255..8ac0ba5da643 100644 --- a/crates/c-api/tests/component/utils.h +++ b/crates/c-api/tests/component/utils.h @@ -1,15 +1,6 @@ #pragma once #include -#define CHECK_ERR(err) \ - do { \ - if (err) { \ - auto msg = wasm_name_t{}; \ - wasmtime_error_message(err, &msg); \ - EXPECT_EQ(err, nullptr) << std::string_view{msg.data, msg.size}; \ - } \ - } while (false) - // From crates/component-util/src/lib.rs inline constexpr std::string_view REALLOC_AND_FREE = R"END( diff --git a/crates/c-api/tests/component/values.cc b/crates/c-api/tests/component/values.cc index 282b08985b7b..e880cc2f9d8f 100644 --- a/crates/c-api/tests/component/values.cc +++ b/crates/c-api/tests/component/values.cc @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include @@ -11,8 +11,11 @@ #include #include -using namespace wasmtime; using namespace wasmtime::component; +using wasmtime::Engine; +using wasmtime::Result; +using wasmtime::Span; +using wasmtime::Store; static std::string echo_component(std::string_view type, std::string_view func, std::string_view host_params) { @@ -60,45 +63,32 @@ static std::string echo_component(std::string_view type, std::string_view func, struct Context { Engine engine; Store store; - wasmtime_context_t *context; + Store::Context context; Component component; - wasmtime_component_instance_t instance; - wasmtime_component_func_t func; + Instance instance; + Func func; }; +typedef Result (*host_func_t)(Store::Context, Span, + Span); + static Context create(std::string_view type, std::string_view body, - std::string_view host_params, - wasmtime_component_func_callback_t callback) { + std::string_view host_params, host_func_t callback) { auto component_text = echo_component(type, body, host_params); Engine engine; Store store(engine); - const auto context = store.context().capi(); + const auto context = store.context(); Component component = Component::compile(engine, component_text).unwrap(); auto f = component.export_index(nullptr, "call"); EXPECT_TRUE(f); - const auto linker = wasmtime_component_linker_new(engine.capi()); - const auto root = wasmtime_component_linker_root(linker); - - wasmtime_component_linker_instance_add_func(root, "do", strlen("do"), - callback, nullptr, nullptr); - - wasmtime_component_linker_instance_delete(root); - - wasmtime_component_instance_t instance = {}; - auto err = wasmtime_component_linker_instantiate(linker, context, - component.capi(), &instance); - CHECK_ERR(err); + Linker linker(engine); + linker.root().add_func("do", callback).unwrap(); - wasmtime_component_linker_delete(linker); - - wasmtime_component_func_t func = {}; - const auto found = wasmtime_component_instance_get_func(&instance, context, - f->capi(), &func); - EXPECT_TRUE(found); - EXPECT_NE(func.store_id, 0); + auto instance = linker.instantiate(context, component).unwrap(); + auto func = *instance.get_func(context, *f); return Context{ .engine = engine, @@ -111,41 +101,29 @@ static Context create(std::string_view type, std::string_view body, } TEST(component, value_record) { - static const auto check = [](const wasmtime_component_val_t &val, uint64_t x, - uint64_t y) { - EXPECT_EQ(val.kind, WASMTIME_COMPONENT_RECORD); - - EXPECT_EQ(val.of.record.size, 2); - const auto entries = val.of.record.data; - - EXPECT_EQ((std::string_view{entries[0].name.data, entries[0].name.size}), - "x"); - EXPECT_EQ(entries[0].val.kind, WASMTIME_COMPONENT_U64); - EXPECT_EQ(entries[0].val.of.u64, x); - - EXPECT_EQ((std::string_view{entries[1].name.data, entries[1].name.size}), - "y"); - EXPECT_EQ(entries[1].val.kind, WASMTIME_COMPONENT_U64); - EXPECT_EQ(entries[1].val.of.u64, y); + static const auto check = [](const Val &v, uint64_t x, uint64_t y) { + EXPECT_TRUE(v.is_record()); + const Record &r = v.get_record(); + EXPECT_EQ(r.size(), 2); + + const auto &x_field = *r.begin(); + EXPECT_EQ(x_field.name(), "x"); + const auto &x_field_val = x_field.value(); + EXPECT_TRUE(x_field_val.is_u64()); + EXPECT_EQ(x_field_val.get_u64(), x); + + const auto &y_field = *(r.begin() + 1); + EXPECT_EQ(y_field.name(), "y"); + const auto &y_field_val = y_field.value(); + EXPECT_TRUE(y_field_val.is_u64()); + EXPECT_EQ(y_field_val.get_u64(), y); }; - static const auto make = [](uint64_t x, - uint64_t y) -> wasmtime_component_val_t { - auto ret = wasmtime_component_val_t{ - .kind = WASMTIME_COMPONENT_RECORD, - }; - - wasmtime_component_valrecord_new_uninit(&ret.of.record, 2); - - const auto entries = ret.of.record.data; - wasm_name_new_from_string(&entries[0].name, "x"); - entries[0].val.kind = WASMTIME_COMPONENT_U64; - entries[0].val.of.u64 = x; - wasm_name_new_from_string(&entries[1].name, "y"); - entries[1].val.kind = WASMTIME_COMPONENT_U64; - entries[1].val.of.u64 = y; - - return ret; + static const auto make = [](uint64_t x, uint64_t y) -> Val { + return Record({ + {"x", x}, + {"y", y}, + }); }; auto ctx = create( @@ -166,50 +144,35 @@ call $do local.get $res )", "(param i64 i64 i32)", - +[](void *, wasmtime_context_t *, const wasmtime_component_val_t *args, - size_t args_len, wasmtime_component_val_t *rets, - size_t rets_len) -> wasmtime_error_t * { - EXPECT_EQ(args_len, 1); + +[](Store::Context, Span args, + Span rets) -> Result { + EXPECT_EQ(args.size(), 1); check(args[0], 1, 2); - EXPECT_EQ(rets_len, 1); + EXPECT_EQ(rets.size(), 1); rets[0] = make(3, 4); - return nullptr; + return std::monostate(); }); auto arg = make(1, 2); - auto res = wasmtime_component_val_t{}; - - auto err = - wasmtime_component_func_call(&ctx.func, ctx.context, &arg, 1, &res, 1); - CHECK_ERR(err); + auto res = Val(false); - err = wasmtime_component_func_post_return(&ctx.func, ctx.context); - CHECK_ERR(err); + ctx.func.call(ctx.context, Span(&arg, 1), Span(&res, 1)) + .unwrap(); + ctx.func.post_return(ctx.context).unwrap(); check(res, 3, 4); - - wasmtime_component_val_delete(&arg); - wasmtime_component_val_delete(&res); } TEST(component, value_string) { - static const auto check = [](const wasmtime_component_val_t &val, - std::string_view text) { - EXPECT_EQ(val.kind, WASMTIME_COMPONENT_STRING); - EXPECT_EQ((std::string_view{val.of.string.data, val.of.string.size}), text); + static const auto check = [](const Val &v, std::string_view text) { + EXPECT_TRUE(v.is_string()); + EXPECT_EQ(v.get_string(), text); }; - static const auto make = - [](std::string_view text) -> wasmtime_component_val_t { - auto str = wasm_name_t{}; - wasm_name_new_from_string(&str, text.data()); - - return wasmtime_component_val_t{ - .kind = WASMTIME_COMPONENT_STRING, - .of = {.string = str}, - }; + static const auto make = [](std::string_view text) -> Val { + return Val::string(text); }; auto ctx = create( @@ -230,62 +193,42 @@ call $do local.get $res )", "(param i32 i32 i32)", - +[](void *, wasmtime_context_t *, const wasmtime_component_val_t *args, - size_t args_len, wasmtime_component_val_t *rets, - size_t rets_len) -> wasmtime_error_t * { - EXPECT_EQ(args_len, 1); + +[](Store::Context, Span args, + Span rets) -> Result { + EXPECT_EQ(args.size(), 1); check(args[0], "hello from A!"); - EXPECT_EQ(rets_len, 1); + EXPECT_EQ(rets.size(), 1); rets[0] = make("hello from B!"); - return nullptr; + return std::monostate(); }); auto arg = make("hello from A!"); - auto res = wasmtime_component_val_t{}; + auto res = Val(false); - auto err = - wasmtime_component_func_call(&ctx.func, ctx.context, &arg, 1, &res, 1); - CHECK_ERR(err); - - err = wasmtime_component_func_post_return(&ctx.func, ctx.context); - CHECK_ERR(err); + ctx.func.call(ctx.context, Span(&arg, 1), Span(&res, 1)) + .unwrap(); + ctx.func.post_return(ctx.context).unwrap(); check(res, "hello from B!"); - - wasmtime_component_val_delete(&arg); - wasmtime_component_val_delete(&res); } TEST(component, value_list) { - static const auto check = [](const wasmtime_component_val_t &val, - std::vector data) { - EXPECT_EQ(val.kind, WASMTIME_COMPONENT_LIST); - auto vals = std::span{val.of.list.data, val.of.list.size}; - EXPECT_EQ(vals.size(), data.size()); - for (auto i = 0; i < data.size(); i++) { - EXPECT_EQ(vals[i].kind, WASMTIME_COMPONENT_U32); - EXPECT_EQ(vals[i].of.u32, data[i]); - } - }; - - static const auto make = - [](std::vector data) -> wasmtime_component_val_t { - auto ret = wasmtime_component_val_t{ - .kind = WASMTIME_COMPONENT_LIST, - }; - - wasmtime_component_vallist_new_uninit(&ret.of.list, data.size()); + static const auto check = [](const Val &v, std::vector data) { + EXPECT_TRUE(v.is_list()); + const List &l = v.get_list(); + EXPECT_EQ(l.size(), data.size()); for (auto i = 0; i < data.size(); i++) { - ret.of.list.data[i] = wasmtime_component_val_t{ - .kind = WASMTIME_COMPONENT_U32, - .of = {.u32 = data[i]}, - }; + const auto &elem = l.begin()[i]; + EXPECT_TRUE(elem.is_u32()); + EXPECT_EQ(elem.get_u32(), data[i]); } + }; - return ret; + static const auto make = [](std::vector data) -> Val { + return List(data); }; auto ctx = create( @@ -306,62 +249,41 @@ call $do local.get $res )", "(param i32 i32 i32)", - +[](void *, wasmtime_context_t *, const wasmtime_component_val_t *args, - size_t args_len, wasmtime_component_val_t *rets, - size_t rets_len) -> wasmtime_error_t * { - EXPECT_EQ(args_len, 1); + +[](Store::Context, Span args, + Span rets) -> Result { + EXPECT_EQ(args.size(), 1); check(args[0], {1, 2, 3}); - EXPECT_EQ(rets_len, 1); - rets[0] = make({4, 5, 6, 7}); + EXPECT_EQ(rets.size(), 1); + rets[0] = make({uint32_t(4), uint32_t(5), uint32_t(6), uint32_t(7)}); - return nullptr; + return std::monostate(); }); - auto arg = make({1, 2, 3}); - auto res = wasmtime_component_val_t{}; - - auto err = - wasmtime_component_func_call(&ctx.func, ctx.context, &arg, 1, &res, 1); - CHECK_ERR(err); + auto arg = make({uint32_t(1), uint32_t(2), uint32_t(3)}); + auto res = Val(false); - err = wasmtime_component_func_post_return(&ctx.func, ctx.context); - CHECK_ERR(err); + ctx.func.call(ctx.context, Span(&arg, 1), Span(&res, 1)) + .unwrap(); + ctx.func.post_return(ctx.context).unwrap(); check(res, {4, 5, 6, 7}); - - wasmtime_component_val_delete(&arg); - wasmtime_component_val_delete(&res); } TEST(component, value_tuple) { - static const auto check = [](const wasmtime_component_val_t &val, - std::vector data) { - EXPECT_EQ(val.kind, WASMTIME_COMPONENT_TUPLE); - auto vals = std::span{val.of.tuple.data, val.of.tuple.size}; - EXPECT_EQ(vals.size(), data.size()); + static const auto check = [](const Val &v, std::vector data) { + EXPECT_TRUE(v.is_tuple()); + const Tuple &t = v.get_tuple(); + EXPECT_EQ(t.size(), data.size()); for (auto i = 0; i < data.size(); i++) { - EXPECT_EQ(vals[i].kind, WASMTIME_COMPONENT_U32); - EXPECT_EQ(vals[i].of.u32, data[i]); + const auto &elem = t.begin()[i]; + EXPECT_TRUE(elem.is_u32()); + EXPECT_EQ(elem.get_u32(), data[i]); } }; - static const auto make = - [](std::vector data) -> wasmtime_component_val_t { - auto ret = wasmtime_component_val_t{ - .kind = WASMTIME_COMPONENT_TUPLE, - }; - - wasmtime_component_valtuple_new_uninit(&ret.of.tuple, data.size()); - - for (auto i = 0; i < data.size(); i++) { - ret.of.list.data[i] = wasmtime_component_val_t{ - .kind = WASMTIME_COMPONENT_U32, - .of = {.u32 = data[i]}, - }; - } - - return ret; + static const auto make = [](std::vector data) -> Val { + return Tuple(data); }; auto ctx = create( @@ -384,90 +306,52 @@ call $do local.get $res )", "(param i32 i32 i32 i32)", - +[](void *, wasmtime_context_t *, const wasmtime_component_val_t *args, - size_t args_len, wasmtime_component_val_t *rets, - size_t rets_len) -> wasmtime_error_t * { - EXPECT_EQ(args_len, 1); + +[](Store::Context, Span args, + Span rets) -> Result { + EXPECT_EQ(args.size(), 1); check(args[0], {1, 2, 3}); - EXPECT_EQ(rets_len, 1); - rets[0] = make({4, 5, 6}); + EXPECT_EQ(rets.size(), 1); + rets[0] = make({uint32_t(4), uint32_t(5), uint32_t(6)}); - return nullptr; + return std::monostate(); }); - auto arg = make({1, 2, 3}); - auto res = wasmtime_component_val_t{}; + auto arg = make({uint32_t(1), uint32_t(2), uint32_t(3)}); + auto res = Val(false); - auto err = - wasmtime_component_func_call(&ctx.func, ctx.context, &arg, 1, &res, 1); - CHECK_ERR(err); - - err = wasmtime_component_func_post_return(&ctx.func, ctx.context); - CHECK_ERR(err); + ctx.func.call(ctx.context, Span(&arg, 1), Span(&res, 1)) + .unwrap(); + ctx.func.post_return(ctx.context).unwrap(); check(res, {4, 5, 6}); - - wasmtime_component_val_delete(&arg); - wasmtime_component_val_delete(&res); } TEST(component, value_variant) { - static const auto check_aa = [](const wasmtime_component_val_t &val, - uint32_t value) { - EXPECT_EQ(val.kind, WASMTIME_COMPONENT_VARIANT); - EXPECT_EQ((std::string_view{val.of.variant.discriminant.data, - val.of.variant.discriminant.size}), - "aa"); - - EXPECT_NE(val.of.variant.val, nullptr); - - EXPECT_EQ(val.of.variant.val->kind, WASMTIME_COMPONENT_U32); - EXPECT_EQ(val.of.variant.val->of.u32, value); + static const auto check_aa = [](const Val &v, uint32_t value) { + EXPECT_TRUE(v.is_variant()); + const Variant &var = v.get_variant(); + EXPECT_EQ(var.discriminant(), "aa"); + EXPECT_NE(var.value(), nullptr); + EXPECT_TRUE(var.value()->is_u32()); + EXPECT_EQ(var.value()->get_u32(), value); }; - static const auto check_bb = [](const wasmtime_component_val_t &val, - std::string_view value) { - EXPECT_EQ(val.kind, WASMTIME_COMPONENT_VARIANT); - EXPECT_EQ((std::string_view{val.of.variant.discriminant.data, - val.of.variant.discriminant.size}), - "bb"); - - EXPECT_NE(val.of.variant.val, nullptr); - - EXPECT_EQ(val.of.variant.val->kind, WASMTIME_COMPONENT_STRING); - EXPECT_EQ((std::string_view{val.of.variant.val->of.string.data, - val.of.variant.val->of.string.size}), - value); + static const auto check_bb = [](const Val &v, std::string_view value) { + EXPECT_TRUE(v.is_variant()); + const Variant &var = v.get_variant(); + EXPECT_EQ(var.discriminant(), "bb"); + EXPECT_NE(var.value(), nullptr); + EXPECT_TRUE(var.value()->is_string()); + EXPECT_EQ(var.value()->get_string(), value); }; - static const auto make_aa = [](uint32_t value) -> wasmtime_component_val_t { - auto ret = wasmtime_component_val_t{ - .kind = WASMTIME_COMPONENT_VARIANT, - }; - - wasm_name_new_from_string(&ret.of.variant.discriminant, "aa"); - - ret.of.variant.val = wasmtime_component_val_new(); - ret.of.variant.val->kind = WASMTIME_COMPONENT_U32; - ret.of.variant.val->of.u32 = value; - - return ret; + static const auto make_aa = [](uint32_t value) -> Val { + return Variant("aa", Val(value)); }; - static const auto make_bb = - [](std::string_view value) -> wasmtime_component_val_t { - auto ret = wasmtime_component_val_t{ - .kind = WASMTIME_COMPONENT_VARIANT, - }; - - wasm_name_new_from_string(&ret.of.variant.discriminant, "bb"); - - ret.of.variant.val = wasmtime_component_val_new(); - ret.of.variant.val->kind = WASMTIME_COMPONENT_STRING; - wasm_name_new(&ret.of.variant.val->of.string, value.size(), value.data()); - - return ret; + static const auto make_bb = [](std::string_view value) -> Val { + return Variant("bb", Val::string(value)); }; auto ctx = create( @@ -496,52 +380,35 @@ call $do local.get $res )", "(param i32 i32 i32 i32)", - +[](void *, wasmtime_context_t *, const wasmtime_component_val_t *args, - size_t args_len, wasmtime_component_val_t *rets, - size_t rets_len) -> wasmtime_error_t * { - EXPECT_EQ(args_len, 1); + +[](Store::Context, Span args, + Span rets) -> Result { + EXPECT_EQ(args.size(), 1); check_aa(args[0], 123); - EXPECT_EQ(rets_len, 1); + EXPECT_EQ(rets.size(), 1); rets[0] = make_bb("textt"); - return nullptr; + return std::monostate(); }); auto arg = make_aa(123); - auto res = wasmtime_component_val_t{}; - - auto err = - wasmtime_component_func_call(&ctx.func, ctx.context, &arg, 1, &res, 1); - CHECK_ERR(err); + auto res = Val(false); - err = wasmtime_component_func_post_return(&ctx.func, ctx.context); - CHECK_ERR(err); + ctx.func.call(ctx.context, Span(&arg, 1), Span(&res, 1)) + .unwrap(); + ctx.func.post_return(ctx.context).unwrap(); check_bb(res, "textt"); - - wasmtime_component_val_delete(&arg); - wasmtime_component_val_delete(&res); } TEST(component, value_enum) { - static const auto check = [](const wasmtime_component_val_t &val, - std::string_view text) { - EXPECT_EQ(val.kind, WASMTIME_COMPONENT_ENUM); - EXPECT_EQ( - (std::string_view{val.of.enumeration.data, val.of.enumeration.size}), - text); + static const auto check = [](const Val &v, std::string_view text) { + EXPECT_TRUE(v.is_enum()); + EXPECT_EQ(v.get_enum(), text); }; - static const auto make = - [](std::string_view text) -> wasmtime_component_val_t { - auto ret = wasmtime_component_val_t{ - .kind = WASMTIME_COMPONENT_ENUM, - }; - - wasm_name_new(&ret.of.enumeration, text.size(), text.data()); - - return ret; + static const auto make = [](std::string_view text) -> Val { + return Val::enum_(text); }; auto ctx = create( @@ -552,64 +419,45 @@ local.get $x call $do )", "(param i32) (result i32)", - +[](void *, wasmtime_context_t *, const wasmtime_component_val_t *args, - size_t args_len, wasmtime_component_val_t *rets, - size_t rets_len) -> wasmtime_error_t * { - EXPECT_EQ(args_len, 1); + +[](Store::Context, Span args, + Span rets) -> Result { + EXPECT_EQ(args.size(), 1); check(args[0], "aa"); - EXPECT_EQ(rets_len, 1); + EXPECT_EQ(rets.size(), 1); rets[0] = make("bb"); - return nullptr; + return std::monostate(); }); auto arg = make("aa"); - auto res = wasmtime_component_val_t{}; + auto res = Val(false); - auto err = - wasmtime_component_func_call(&ctx.func, ctx.context, &arg, 1, &res, 1); - CHECK_ERR(err); - - err = wasmtime_component_func_post_return(&ctx.func, ctx.context); - CHECK_ERR(err); + ctx.func.call(ctx.context, Span(&arg, 1), Span(&res, 1)) + .unwrap(); + ctx.func.post_return(ctx.context).unwrap(); check(res, "bb"); - - wasmtime_component_val_delete(&arg); - wasmtime_component_val_delete(&res); } TEST(component, value_option) { - static const auto check = [](const wasmtime_component_val_t &val, - std::optional value) { - EXPECT_EQ(val.kind, WASMTIME_COMPONENT_OPTION); - + static const auto check = [](const Val &v, std::optional value) { + EXPECT_TRUE(v.is_option()); + const WitOption &o = v.get_option(); if (value.has_value()) { - EXPECT_NE(val.of.option, nullptr); - EXPECT_EQ(val.of.option->kind, WASMTIME_COMPONENT_U32); - EXPECT_EQ(val.of.option->of.u32, *value); + EXPECT_NE(o.value(), nullptr); + EXPECT_TRUE(o.value()->is_u32()); + EXPECT_EQ(o.value()->get_u32(), *value); } else { - EXPECT_EQ(val.of.option, nullptr); + EXPECT_EQ(o.value(), nullptr); } }; - static const auto make = - [](std::optional value) -> wasmtime_component_val_t { - auto ret = wasmtime_component_val_t{ - .kind = WASMTIME_COMPONENT_OPTION, - .of = {.option = nullptr}, - }; - - if (value.has_value()) { - ret.of.option = wasmtime_component_val_new(); - *ret.of.option = wasmtime_component_val_t{ - .kind = WASMTIME_COMPONENT_U32, - .of = {.u32 = *value}, - }; + static const auto make = [](std::optional value) -> Val { + if (value) { + return WitOption(Val(*value)); } - - return ret; + return WitOption(std::nullopt); }; auto ctx = create( @@ -630,62 +478,43 @@ call $do local.get $res )", "(param i32 i32 i32)", - +[](void *, wasmtime_context_t *, const wasmtime_component_val_t *args, - size_t args_len, wasmtime_component_val_t *rets, - size_t rets_len) -> wasmtime_error_t * { - EXPECT_EQ(args_len, 1); + +[](Store::Context, Span args, + Span rets) -> Result { + EXPECT_EQ(args.size(), 1); check(args[0], 123); - EXPECT_EQ(rets_len, 1); + EXPECT_EQ(rets.size(), 1); rets[0] = make({}); - return nullptr; + return std::monostate(); }); auto arg = make(123); - auto res = wasmtime_component_val_t{}; + auto res = Val(false); - auto err = - wasmtime_component_func_call(&ctx.func, ctx.context, &arg, 1, &res, 1); - CHECK_ERR(err); - - err = wasmtime_component_func_post_return(&ctx.func, ctx.context); - CHECK_ERR(err); + ctx.func.call(ctx.context, Span(&arg, 1), Span(&res, 1)) + .unwrap(); + ctx.func.post_return(ctx.context).unwrap(); check(res, {}); - - wasmtime_component_val_delete(&arg); - wasmtime_component_val_delete(&res); } TEST(component, value_result) { - static const auto check = [](const wasmtime_component_val_t &val, - bool expected_is_ok, uint32_t expected_value) { - EXPECT_EQ(val.kind, WASMTIME_COMPONENT_RESULT); - - EXPECT_EQ(val.of.result.is_ok, expected_is_ok); - EXPECT_NE(val.of.result.val, nullptr); - - EXPECT_EQ(val.of.result.val->kind, WASMTIME_COMPONENT_U32); - EXPECT_EQ(val.of.result.val->of.u32, expected_value); + static const auto check = [](const Val &v, bool expected_is_ok, + uint32_t expected_value) { + EXPECT_TRUE(v.is_result()); + const WitResult &r = v.get_result(); + EXPECT_EQ(r.is_ok(), expected_is_ok); + EXPECT_NE(r.payload(), nullptr); + EXPECT_TRUE(r.payload()->is_u32()); + EXPECT_EQ(r.payload()->get_u32(), expected_value); }; - static const auto make = [](bool is_ok, - uint32_t value) -> wasmtime_component_val_t { - auto ret = wasmtime_component_val_t{ - .kind = WASMTIME_COMPONENT_RESULT, - }; - - const auto inner = wasmtime_component_val_new(); - inner->kind = WASMTIME_COMPONENT_U32; - inner->of.u32 = value; - - ret.of.result = { - .is_ok = is_ok, - .val = inner, - }; - - return ret; + static const auto make = [](bool is_ok, uint32_t value) -> Val { + if (is_ok) { + return WitResult::ok(Val(value)); + } + return WitResult::err(Val(value)); }; auto ctx = create( @@ -706,58 +535,40 @@ call $do local.get $res )", "(param i32 i32 i32)", - +[](void *, wasmtime_context_t *, const wasmtime_component_val_t *args, - size_t args_len, wasmtime_component_val_t *rets, - size_t rets_len) -> wasmtime_error_t * { - EXPECT_EQ(args_len, 1); + +[](Store::Context, Span args, + Span rets) -> Result { + EXPECT_EQ(args.size(), 1); check(args[0], true, 123); - EXPECT_EQ(rets_len, 1); + EXPECT_EQ(rets.size(), 1); rets[0] = make(false, 456); - return nullptr; + return std::monostate(); }); auto arg = make(true, 123); - auto res = wasmtime_component_val_t{}; - - auto err = - wasmtime_component_func_call(&ctx.func, ctx.context, &arg, 1, &res, 1); - CHECK_ERR(err); + auto res = Val(false); - err = wasmtime_component_func_post_return(&ctx.func, ctx.context); - CHECK_ERR(err); + ctx.func.call(ctx.context, Span(&arg, 1), Span(&res, 1)) + .unwrap(); + ctx.func.post_return(ctx.context).unwrap(); check(res, false, 456); - - wasmtime_component_val_delete(&arg); - wasmtime_component_val_delete(&res); } TEST(component, value_flags) { - static const auto check = [](const wasmtime_component_val_t &val, - std::vector data) { - EXPECT_EQ(val.kind, WASMTIME_COMPONENT_FLAGS); - auto flags = std::span{val.of.flags.data, val.of.flags.size}; - EXPECT_EQ(flags.size(), data.size()); - for (auto i = 0; i < data.size(); i++) { - EXPECT_EQ((std::string_view{flags[i].data, flags[i].size}), data[i]); - } - }; - - static const auto make = - [](std::vector data) -> wasmtime_component_val_t { - auto ret = wasmtime_component_val_t{ - .kind = WASMTIME_COMPONENT_FLAGS, - }; - - wasmtime_component_valflags_new_uninit(&ret.of.flags, data.size()); + static const auto check = [](const Val &v, std::vector data) { + EXPECT_TRUE(v.is_flags()); + const Flags &f = v.get_flags(); + EXPECT_EQ(f.size(), data.size()); for (auto i = 0; i < data.size(); i++) { - wasm_name_new(&ret.of.flags.data[i], data[i].size(), data[i].data()); + EXPECT_EQ(f.begin()[i].name(), data[i]); } + }; - return ret; + static const auto make = [](std::vector data) -> Val { + return Flags(data); }; auto ctx = create( @@ -768,76 +579,274 @@ local.get $x call $do )", "(param i32) (result i32)", - +[](void *, wasmtime_context_t *, const wasmtime_component_val_t *args, - size_t args_len, wasmtime_component_val_t *rets, - size_t rets_len) -> wasmtime_error_t * { - EXPECT_EQ(args_len, 1); + +[](Store::Context, Span args, + Span rets) -> Result { + EXPECT_EQ(args.size(), 1); check(args[0], {"aa"}); - EXPECT_EQ(rets_len, 1); - rets[0] = make({"aa", "bb"}); + EXPECT_EQ(rets.size(), 1); + rets[0] = make({Flag("aa"), Flag("bb")}); - return nullptr; + return std::monostate(); }); - auto arg = make({"aa"}); - auto res = wasmtime_component_val_t{}; + auto arg = make({Flag("aa")}); + auto res = Val(false); - auto err = - wasmtime_component_func_call(&ctx.func, ctx.context, &arg, 1, &res, 1); - CHECK_ERR(err); - - err = wasmtime_component_func_post_return(&ctx.func, ctx.context); - CHECK_ERR(err); + ctx.func.call(ctx.context, Span(&arg, 1), Span(&res, 1)) + .unwrap(); + ctx.func.post_return(ctx.context).unwrap(); check(res, {"aa", "bb"}); - - wasmtime_component_val_delete(&arg); - wasmtime_component_val_delete(&res); } TEST(component, value_list_inner) { - { - auto x = wasmtime_component_val_t{ - .kind = WASMTIME_COMPONENT_LIST, - }; - wasmtime_component_vallist_new_empty(&x.of.list); - EXPECT_EQ(x.of.list.data, nullptr); - EXPECT_EQ(x.of.list.size, 0); + auto x = wasmtime_component_val_t{ + .kind = WASMTIME_COMPONENT_LIST, + }; + wasmtime_component_vallist_new_empty(&x.of.list); + EXPECT_EQ(x.of.list.data, nullptr); + EXPECT_EQ(x.of.list.size, 0); + + wasmtime_component_vallist_new_uninit(&x.of.list, 1); + EXPECT_NE(x.of.list.data, nullptr); + EXPECT_EQ(x.of.list.size, 1); + + wasmtime_component_vallist_delete(&x.of.list); + + auto items = std::array{ + wasmtime_component_val_t{ + .kind = WASMTIME_COMPONENT_U32, + .of = {.u32 = 123}, + }, + }; + + wasmtime_component_vallist_new(&x.of.list, items.size(), items.data()); + EXPECT_NE(x.of.list.data, nullptr); + EXPECT_EQ(x.of.list.size, 1); + + EXPECT_EQ(x.of.list.data[0].kind, WASMTIME_COMPONENT_U32); + EXPECT_EQ(x.of.list.data[0].of.u32, 123); + + auto clone = wasmtime_component_val_t{ + .kind = WASMTIME_COMPONENT_LIST, + }; + + wasmtime_component_vallist_copy(&clone.of.list, &x.of.list); + wasmtime_component_vallist_delete(&x.of.list); + + EXPECT_NE(clone.of.list.data, nullptr); + EXPECT_EQ(clone.of.list.size, 1); + + EXPECT_EQ(clone.of.list.data[0].kind, WASMTIME_COMPONENT_U32); + EXPECT_EQ(clone.of.list.data[0].of.u32, 123); + + wasmtime_component_vallist_delete(&clone.of.list); +} - wasmtime_component_vallist_new_uninit(&x.of.list, 1); - EXPECT_NE(x.of.list.data, nullptr); - EXPECT_EQ(x.of.list.size, 1); +TEST(component, records) { + Record r({{"x", uint64_t(1)}, {"y", uint64_t(2)}}); + EXPECT_EQ(r.size(), 2); - wasmtime_component_vallist_delete(&x.of.list); + for (auto &field : r) { + if (field.name() == "x") { + EXPECT_EQ(field.value().get_u64(), 1); + } else if (field.name() == "y") { + EXPECT_EQ(field.value().get_u64(), 2); + } else { + FAIL() << "unexpected field name: " << field.name(); + } + } + + Record r2({{"x", r}, {"y", uint64_t(2)}}); + EXPECT_EQ(r2.size(), 2); + EXPECT_EQ(r.size(), 2); + + for (auto &field : r2) { + if (field.name() == "x") { + auto inner = field.value().get_record(); + EXPECT_EQ(inner.size(), 2); + for (auto &inner_field : inner) { + if (inner_field.name() == "x") { + EXPECT_EQ(inner_field.value().get_u64(), 1); + } else if (inner_field.name() == "y") { + EXPECT_EQ(inner_field.value().get_u64(), 2); + } else { + FAIL() << "unexpected inner field name: " << inner_field.name(); + } + } + } else if (field.name() == "y") { + EXPECT_EQ(field.value().get_u64(), 2); + } else { + FAIL() << "unexpected field name: " << field.name(); + } + } + + Val record = r2; + EXPECT_TRUE(record.is_record()); + EXPECT_EQ(r2.size(), 2); + Val record2 = std::move(r2); + EXPECT_TRUE(record2.is_record()); + EXPECT_EQ(r2.size(), 0); +} + +TEST(component, lists) { + List l({uint32_t(1), uint32_t(2), uint32_t(3)}); + EXPECT_EQ(l.size(), 3); + uint32_t expected = 1; + for (auto &val : l) { + EXPECT_EQ(val.get_u32(), expected); + expected++; + } + + List l2 = l; + EXPECT_EQ(l.size(), 3); + EXPECT_EQ(l2.size(), 3); + + List l3 = std::move(l); + EXPECT_EQ(l.size(), 0); + EXPECT_EQ(l3.size(), 3); + + Val value(l3); + value.get_list(); +} - auto items = std::array{ - wasmtime_component_val_t{ - .kind = WASMTIME_COMPONENT_U32, - .of = {.u32 = 123}, - }, - }; +TEST(component, tuples) { + Tuple l({uint32_t(1), uint64_t(2), uint8_t(3)}); + EXPECT_EQ(l.size(), 3); + + Val value(l); + EXPECT_TRUE(value.is_tuple()); + EXPECT_EQ(l.size(), 3); + + for (auto &val : l) { + if (val.is_u32()) { + EXPECT_EQ(val.get_u32(), 1); + } else if (val.is_u64()) { + EXPECT_EQ(val.get_u64(), 2); + } else if (val.is_u8()) { + EXPECT_EQ(val.get_u8(), 3); + } else { + FAIL() << "unexpected tuple value type"; + } + } +} + +TEST(component, variants) { + Variant v("hello", uint32_t(42)); + EXPECT_EQ(v.discriminant(), "hello"); + EXPECT_TRUE(v.value()->is_u32()); + EXPECT_EQ(v.value()->get_u32(), 42); + + Variant v2("another", v); + EXPECT_EQ(v.discriminant(), "hello"); + EXPECT_TRUE(v.value()->is_u32()); + EXPECT_EQ(v.value()->get_u32(), 42); + EXPECT_EQ(v2.discriminant(), "another"); + EXPECT_TRUE(v2.value()->is_variant()); + auto inner = v2.value()->get_variant(); + EXPECT_EQ(inner.discriminant(), "hello"); + EXPECT_TRUE(inner.value()->is_u32()); + EXPECT_EQ(inner.value()->get_u32(), 42); + + Val value = v; + EXPECT_TRUE(value.is_variant()); + auto v3 = value.get_variant(); + EXPECT_EQ(v3.discriminant(), "hello"); + EXPECT_TRUE(v3.value()->is_u32()); + EXPECT_EQ(v3.value()->get_u32(), 42); +} - wasmtime_component_vallist_new(&x.of.list, items.size(), items.data()); - EXPECT_NE(x.of.list.data, nullptr); - EXPECT_EQ(x.of.list.size, 1); +TEST(component, strings) { + Val v = Val::string("hi"); + EXPECT_TRUE(v.is_string()); + EXPECT_EQ(v.get_string(), "hi"); - EXPECT_EQ(x.of.list.data[0].kind, WASMTIME_COMPONENT_U32); - EXPECT_EQ(x.of.list.data[0].of.u32, 123); + v = Val::string("another"); + EXPECT_TRUE(v.is_string()); + EXPECT_EQ(v.get_string(), "another"); +} - auto clone = wasmtime_component_val_t{ - .kind = WASMTIME_COMPONENT_LIST, - }; +TEST(component, results) { + WitResult r = WitResult::ok(uint32_t(42)); + EXPECT_TRUE(r.is_ok()); + EXPECT_EQ(r.payload()->get_u32(), 42); + + r = WitResult::ok(std::nullopt); + EXPECT_TRUE(r.is_ok()); + EXPECT_EQ(r.payload(), nullptr); + + r = WitResult::err(std::nullopt); + EXPECT_FALSE(r.is_ok()); + EXPECT_EQ(r.payload(), nullptr); + + Val v = r; + EXPECT_TRUE(v.is_result()); + auto r2 = v.get_result(); + EXPECT_FALSE(r2.is_ok()); + EXPECT_EQ(r2.payload(), nullptr); + + r = WitResult::ok(uint32_t(99)); + v = r; + EXPECT_TRUE(r.is_ok()); + EXPECT_NE(r.payload(), nullptr); + EXPECT_EQ(r.payload()->get_u32(), 99); +} - wasmtime_component_vallist_copy(&clone.of.list, &x.of.list); - wasmtime_component_vallist_delete(&x.of.list); +TEST(component, enums) { + Val v = Val::enum_("hi"); + EXPECT_TRUE(v.is_enum()); + EXPECT_EQ(v.get_enum(), "hi"); - EXPECT_NE(clone.of.list.data, nullptr); - EXPECT_EQ(clone.of.list.size, 1); + v = Val::enum_("another"); + EXPECT_TRUE(v.is_enum()); + EXPECT_EQ(v.get_enum(), "another"); +} - EXPECT_EQ(clone.of.list.data[0].kind, WASMTIME_COMPONENT_U32); - EXPECT_EQ(clone.of.list.data[0].of.u32, 123); +TEST(component, options) { + WitOption o(Val(uint32_t(42))); + EXPECT_NE(o.value(), nullptr); + EXPECT_TRUE(o.value()->is_u32()); + EXPECT_EQ(o.value()->get_u32(), 42); + + Val v(o); + WitOption o2(v); + EXPECT_NE(o.value(), nullptr); + EXPECT_TRUE(o2.value()->is_option()); + auto inner = o2.value()->get_option(); + auto value = inner.value(); + EXPECT_NE(value, nullptr); + EXPECT_TRUE(value->is_u32()); + EXPECT_EQ(value->get_u32(), 42); + + EXPECT_NE(o.value(), nullptr); + EXPECT_TRUE(o.value()->is_u32()); + EXPECT_EQ(o.value()->get_u32(), 42); + + WitOption o3(std::nullopt); + EXPECT_EQ(o3.value(), nullptr); +} - wasmtime_component_vallist_delete(&clone.of.list); +TEST(component, flags) { + std::vector flags = { + Flag("a"), + Flag("b"), + Flag("c"), + }; + Flags f(flags); + EXPECT_EQ(f.size(), 3); + for (auto i = 0; i < f.size(); i++) { + EXPECT_EQ(f.begin()[i].name(), flags[i].name()); } + + flags.clear(); + Flags f2(flags); + EXPECT_EQ(f2.size(), 0); + EXPECT_EQ(f.size(), 3); + + Val v = f; + EXPECT_TRUE(v.is_flags()); + Flags f3 = v.get_flags(); + EXPECT_EQ(f3.size(), 3); + EXPECT_EQ(f.size(), 3); } diff --git a/crates/c-api/tests/linker.cc b/crates/c-api/tests/linker.cc index f0f30e4d157a..65d3a2b03820 100644 --- a/crates/c-api/tests/linker.cc +++ b/crates/c-api/tests/linker.cc @@ -72,3 +72,37 @@ TEST(Linker, CallableCopy) { CallableFunc cf; linker.func_new("a", "f", FuncType({}, {}), cf).unwrap(); } + +TEST(Linker, DefineUnknownImportsAsTraps) { + Engine engine; + Linker linker(engine); + Store store(engine); + Module mod = + Module::compile( + engine, + "(module (import \"\" \"\" (func)) (func (export \"x\") call 0))") + .unwrap(); + linker.define_unknown_imports_as_traps(mod).unwrap(); + + auto instance = linker.instantiate(store.context(), mod).unwrap(); + Func f = std::get(*instance.get(store.context(), "x")); + TrapError err = f.call(store.context(), {}).err(); + std::get(err.data); +} + +TEST(Linker, DefineUnknownImportsAsDefaultValues) { + Engine engine; + Linker linker(engine); + Store store(engine); + Module mod = + Module::compile( + engine, + "(module (import \"\" \"\" (func)) (func (export \"x\") call 0))") + .unwrap(); + linker.define_unknown_imports_as_default_values(store.context(), mod) + .unwrap(); + + auto instance = linker.instantiate(store.context(), mod).unwrap(); + Func f = std::get(*instance.get(store.context(), "x")); + f.call(store.context(), {}).unwrap(); +} diff --git a/crates/c-api/tests/wasip2.cc b/crates/c-api/tests/wasip2.cc index bc3b3a535518..260161e91c8a 100644 --- a/crates/c-api/tests/wasip2.cc +++ b/crates/c-api/tests/wasip2.cc @@ -1,41 +1,29 @@ -#include "component/utils.h" - #include -#include -#include +#include #include -using namespace wasmtime; using namespace wasmtime::component; TEST(wasip2, smoke) { static constexpr auto component_text = std::string_view{ R"END( -(component) +(component + (import "wasi:cli/environment@0.2.0" (instance + (export "get-arguments" (func (result (list string)))) + )) +) )END", }; - Engine engine; - Store store(engine); + wasmtime::Engine engine; + wasmtime::Store store(engine); auto context = store.context(); - const auto cfg = wasmtime_wasip2_config_new(); - wasmtime_wasip2_config_inherit_stdin(cfg); - wasmtime_wasip2_config_inherit_stdout(cfg); - wasmtime_wasip2_config_inherit_stderr(cfg); - wasmtime_wasip2_config_arg(cfg, "hello", strlen("hello")); - wasmtime_context_set_wasip2(context.capi(), cfg); - + wasmtime::WasiConfig config; + context.set_wasi(std::move(config)).unwrap(); Component component = Component::compile(engine, component_text).unwrap(); - const auto linker = wasmtime_component_linker_new(engine.capi()); - - wasmtime_component_linker_add_wasip2(linker); - - wasmtime_component_instance_t instance = {}; - auto err = wasmtime_component_linker_instantiate(linker, context.capi(), - component.capi(), &instance); - CHECK_ERR(err); - - wasmtime_component_linker_delete(linker); + Linker linker(engine); + linker.add_wasip2().unwrap(); + linker.instantiate(context, component).unwrap(); } diff --git a/crates/wasmtime/src/runtime/linker.rs b/crates/wasmtime/src/runtime/linker.rs index 06702b4c68ce..377f3a606cb3 100644 --- a/crates/wasmtime/src/runtime/linker.rs +++ b/crates/wasmtime/src/runtime/linker.rs @@ -279,26 +279,24 @@ impl Linker { /// ``` pub fn define_unknown_imports_as_default_values( &mut self, - store: &mut impl AsContextMut, + mut store: impl AsContextMut, module: &Module, ) -> anyhow::Result<()> where T: 'static, { + let mut store = store.as_context_mut(); for import in module.imports() { if let Err(import_err) = self._get_by_import(&import) { let default_extern = - import_err - .ty() - .default_value(&mut *store) - .with_context(|| { - anyhow!( - "no default value exists for `{}::{}` with type `{:?}`", - import.module(), - import.name(), - import_err.ty(), - ) - })?; + import_err.ty().default_value(&mut store).with_context(|| { + anyhow!( + "no default value exists for `{}::{}` with type `{:?}`", + import.module(), + import.name(), + import_err.ty(), + ) + })?; self.define( store.as_context(), import.module(), diff --git a/src/commands/run.rs b/src/commands/run.rs index 6c1f411b922c..d7ade1e353ab 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -492,7 +492,7 @@ impl RunCommand { match linker { CliLinker::Core(linker) => { linker.define_unknown_imports_as_default_values( - store, + &mut *store, main_target.unwrap_core(), )?; }