Skip to content

Commit 579ec46

Browse files
authored
capi: Start filling out C++ API for components (#11880)
* capi: Start filling out C++ API for components I'd like to write some fancier tests soon for this, but I'm not a fan of writing C, so this commit starts to fill out the C++ API for components by adding a few wrapper classes. Like the rest of the C++ API this is all built as a wrapper around the C API. This commit introduces a `Component` type and an `ExportIndex` type to go along with it. This binds the `component.h` header file in C++. This also starts a convention of `capi` methods to access the underlying pointer/value as a conventional way to mix the C/C++ APIs which will allow gradually migrating tests to C++. * Fix clippy lint
1 parent dcd6544 commit 579ec46

File tree

15 files changed

+388
-198
lines changed

15 files changed

+388
-198
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Convenience header to include all of `wasmtime/component/*.hh`.
2+
3+
#ifndef WASMTIME_COMPONENT_HH
4+
#define WASMTIME_COMPONENT_HH
5+
6+
#include <wasmtime/component/component.hh>
7+
8+
#endif // WASMTIME_COMPONENT_HH

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,13 @@ wasmtime_component_get_export_index(
129129
const wasmtime_component_export_index_t *instance_export_index,
130130
const char *name, size_t name_len);
131131

132+
/**
133+
* \brief Creates a new separately-owned copy of the specified index.
134+
*/
135+
WASM_API_EXTERN wasmtime_component_export_index_t *
136+
wasmtime_component_export_index_clone(
137+
const wasmtime_component_export_index_t *index);
138+
132139
/**
133140
* \brief Deletes a #wasmtime_component_export_index_t
134141
*
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
/**
2+
* \file wasmtime/component/component.hh
3+
*/
4+
5+
#ifndef WASMTIME_COMPONENT_HH
6+
#define WASMTIME_COMPONENT_HH
7+
8+
#include <wasmtime/conf.h>
9+
10+
#ifdef WASMTIME_FEATURE_COMPONENT_MODEL
11+
12+
#include <memory>
13+
#include <optional>
14+
#include <string_view>
15+
#include <vector>
16+
#include <wasmtime/component/component.h>
17+
#include <wasmtime/engine.hh>
18+
#include <wasmtime/error.hh>
19+
#include <wasmtime/span.hh>
20+
#include <wasmtime/wat.hh>
21+
22+
namespace wasmtime {
23+
namespace component {
24+
25+
/**
26+
* \brief An index to an exported item within a particular component.
27+
*
28+
* This structure is acquired from a `Component` and used to lookup exports on
29+
* instances.
30+
*/
31+
class ExportIndex {
32+
friend class Component;
33+
34+
struct deleter {
35+
void operator()(wasmtime_component_export_index_t *p) const {
36+
wasmtime_component_export_index_delete(p);
37+
}
38+
};
39+
40+
std::unique_ptr<wasmtime_component_export_index_t, deleter> ptr;
41+
42+
ExportIndex(wasmtime_component_export_index_t *raw) : ptr(raw) {}
43+
44+
public:
45+
/// Copies another index into this one.
46+
ExportIndex(const ExportIndex &other)
47+
: ptr(wasmtime_component_export_index_clone(other.ptr.get())) {}
48+
/// Copies another index into this one.
49+
ExportIndex &operator=(const ExportIndex &other) {
50+
ptr.reset(wasmtime_component_export_index_clone(other.ptr.get()));
51+
return *this;
52+
}
53+
54+
~ExportIndex() = default;
55+
/// Moves resources from another component into this one.
56+
ExportIndex(ExportIndex &&other) = default;
57+
/// Moves resources from another component into this one.
58+
ExportIndex &operator=(ExportIndex &&other) = default;
59+
60+
/// \brief Returns the underlying C API pointer.
61+
const wasmtime_component_export_index_t *capi() const { return ptr.get(); }
62+
63+
/// \brief Returns the underlying C API pointer.
64+
wasmtime_component_export_index_t *capi() { return ptr.get(); }
65+
};
66+
67+
/**
68+
* \brief Representation of a compiled WebAssembly component.
69+
*/
70+
class Component {
71+
struct deleter {
72+
void operator()(wasmtime_component_t *p) const {
73+
wasmtime_component_delete(p);
74+
}
75+
};
76+
77+
std::unique_ptr<wasmtime_component_t, deleter> ptr;
78+
79+
Component(wasmtime_component_t *raw) : ptr(raw) {}
80+
81+
public:
82+
/// Copies another component into this one.
83+
Component(const Component &other)
84+
: ptr(wasmtime_component_clone(other.ptr.get())) {}
85+
/// Copies another component into this one.
86+
Component &operator=(const Component &other) {
87+
ptr.reset(wasmtime_component_clone(other.ptr.get()));
88+
return *this;
89+
}
90+
~Component() = default;
91+
/// Moves resources from another component into this one.
92+
Component(Component &&other) = default;
93+
/// Moves resources from another component into this one.
94+
Component &operator=(Component &&other) = default;
95+
96+
/// \brief Returns the underlying C API pointer.
97+
const wasmtime_component_t *capi() const { return ptr.get(); }
98+
99+
/// \brief Returns the underlying C API pointer.
100+
wasmtime_component_t *capi() { return ptr.get(); }
101+
102+
#ifdef WASMTIME_FEATURE_COMPILER
103+
/**
104+
* \brief Compiles a component from the WebAssembly text format.
105+
*
106+
* This function will automatically use `wat2wasm` on the input and then
107+
* delegate to the #compile function.
108+
*/
109+
static Result<Component> compile(Engine &engine, std::string_view wat) {
110+
auto wasm = wat2wasm(wat);
111+
if (!wasm) {
112+
return wasm.err();
113+
}
114+
auto bytes = wasm.ok();
115+
return compile(engine, bytes);
116+
}
117+
118+
/**
119+
* \brief Compiles a component from the WebAssembly binary format.
120+
*
121+
* This function compiles the provided WebAssembly binary specified by `wasm`
122+
* within the compilation settings configured by `engine`. This method is
123+
* synchronous and will not return until the component has finished compiling.
124+
*
125+
* This function can fail if the WebAssembly binary is invalid or doesn't
126+
* validate (or similar). Note that this API does not compile WebAssembly
127+
* modules, which is done with `Module` instead of `Component`.
128+
*/
129+
static Result<Component> compile(Engine &engine, Span<uint8_t> wasm) {
130+
wasmtime_component_t *ret = nullptr;
131+
auto *error =
132+
wasmtime_component_new(engine.capi(), wasm.data(), wasm.size(), &ret);
133+
if (error != nullptr) {
134+
return Error(error);
135+
}
136+
return Component(ret);
137+
}
138+
#endif // WASMTIME_FEATURE_COMPILER
139+
140+
/**
141+
* \brief Deserializes a previous list of bytes created with `serialize`.
142+
*
143+
* This function is intended to be much faster than `compile` where it uses
144+
* the artifacts of a previous compilation to quickly create an in-memory
145+
* component ready for instantiation.
146+
*
147+
* It is not safe to pass arbitrary input to this function, it is only safe to
148+
* pass in output from previous calls to `serialize`. For more information see
149+
* the Rust documentation -
150+
* https://docs.wasmtime.dev/api/wasmtime/struct.Module.html#method.deserialize
151+
*/
152+
static Result<Component> deserialize(Engine &engine, Span<uint8_t> wasm) {
153+
wasmtime_component_t *ret = nullptr;
154+
auto *error = wasmtime_component_deserialize(engine.capi(), wasm.data(),
155+
wasm.size(), &ret);
156+
if (error != nullptr) {
157+
return Error(error);
158+
}
159+
return Component(ret);
160+
}
161+
162+
/**
163+
* \brief Deserializes a component from an on-disk file.
164+
*
165+
* This function is the same as `deserialize` except that it reads the data
166+
* for the serialized component from the path on disk. This can be faster than
167+
* the alternative which may require copying the data around.
168+
*
169+
* It is not safe to pass arbitrary input to this function, it is only safe to
170+
* pass in output from previous calls to `serialize`. For more information see
171+
* the Rust documentation -
172+
* https://docs.wasmtime.dev/api/wasmtime/struct.Module.html#method.deserialize
173+
*/
174+
static Result<Component> deserialize_file(Engine &engine,
175+
const std::string &path) {
176+
wasmtime_component_t *ret = nullptr;
177+
auto *error =
178+
wasmtime_component_deserialize_file(engine.capi(), path.c_str(), &ret);
179+
if (error != nullptr) {
180+
return Error(error);
181+
}
182+
return Component(ret);
183+
}
184+
185+
#ifdef WASMTIME_FEATURE_COMPILER
186+
/**
187+
* \brief Serializes this component to a list of bytes.
188+
*
189+
* The returned bytes can then be used to later pass to `deserialize` to
190+
* quickly recreate this component in a different process perhaps.
191+
*/
192+
Result<std::vector<uint8_t>> serialize() const {
193+
wasm_byte_vec_t bytes;
194+
auto *error = wasmtime_component_serialize(ptr.get(), &bytes);
195+
if (error != nullptr) {
196+
return Error(error);
197+
}
198+
std::vector<uint8_t> ret;
199+
Span<uint8_t> raw(reinterpret_cast<uint8_t *>(bytes.data), bytes.size);
200+
ret.assign(raw.begin(), raw.end());
201+
wasm_byte_vec_delete(&bytes);
202+
return ret;
203+
}
204+
#endif // WASMTIME_FEATURE_COMPILER
205+
206+
/**
207+
* \brief Returns the export index for the export named `name` in this
208+
* component.
209+
*
210+
* The `instance` argument is an optionally provided index which is the
211+
* instance under which the `name` should be looked up.
212+
*/
213+
std::optional<ExportIndex> export_index(ExportIndex *instance,
214+
std::string_view name) {
215+
auto ret = wasmtime_component_get_export_index(
216+
capi(), instance ? instance->capi() : nullptr, name.data(),
217+
name.size());
218+
if (ret) {
219+
return ExportIndex(ret);
220+
}
221+
return std::nullopt;
222+
};
223+
};
224+
225+
} // namespace component
226+
} // namespace wasmtime
227+
228+
#endif // WASMTIME_FEATURE_COMPONENT_MODEL
229+
230+
#endif // WASMTIME_COMPONENT_HH

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ public:
5656

5757
/// \brief Returns whether this engine is using Pulley for execution.
5858
void is_pulley() const { wasmtime_engine_is_pulley(ptr.get()); }
59+
60+
/// \brief Returns the underlying C API pointer.
61+
const wasm_engine_t *capi() const { return ptr.get(); }
62+
63+
/// \brief Returns the underlying C API pointer.
64+
wasm_engine_t *capi() { return ptr.get(); }
5965
};
6066

6167
} // namespace wasmtime

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ template <> struct WasmType<std::optional<ExternRef>> {
108108
return std::nullopt;
109109
}
110110
wasmtime_externref_t val;
111-
wasmtime_externref_from_raw(cx.raw_context(), p->externref, &val);
111+
wasmtime_externref_from_raw(cx.capi(), p->externref, &val);
112112
return ExternRef(val);
113113
}
114114
};
@@ -618,7 +618,7 @@ public:
618618
ptr = reinterpret_cast<wasmtime_val_raw_t *>(alignof(wasmtime_val_raw_t));
619619
WasmTypeList<Params>::store(cx, ptr, params);
620620
wasm_trap_t *trap = nullptr;
621-
auto *error = wasmtime_func_call_unchecked(cx.raw_context(), &f.func, ptr,
621+
auto *error = wasmtime_func_call_unchecked(cx.capi(), &f.func, ptr,
622622
storage.size(), &trap);
623623
if (error != nullptr) {
624624
return TrapError(Error(error));
@@ -666,7 +666,7 @@ template <> struct detail::WasmType<std::optional<Func>> {
666666
static void store(Store::Context cx, wasmtime_val_raw_t *p,
667667
const std::optional<Func> func) {
668668
if (func) {
669-
p->funcref = wasmtime_func_to_raw(cx.raw_context(), &func->capi());
669+
p->funcref = wasmtime_func_to_raw(cx.capi(), &func->capi());
670670
} else {
671671
p->funcref = 0;
672672
}
@@ -677,7 +677,7 @@ template <> struct detail::WasmType<std::optional<Func>> {
677677
return std::nullopt;
678678
}
679679
wasmtime_func_t ret;
680-
wasmtime_func_from_raw(cx.raw_context(), p->funcref, &ret);
680+
wasmtime_func_from_raw(cx.capi(), p->funcref, &ret);
681681
return ret;
682682
}
683683
};

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,12 @@ public:
183183
return ret;
184184
}
185185
#endif // WASMTIME_FEATURE_COMPILER
186+
187+
/// \brief Returns the underlying C API pointer.
188+
const wasmtime_module_t *capi() const { return ptr.get(); }
189+
190+
/// \brief Returns the underlying C API pointer.
191+
wasmtime_module_t *capi() { return ptr.get(); }
186192
};
187193

188194
} // namespace wasmtime

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,11 @@ public:
149149
wasmtime_context_set_epoch_deadline(ptr, ticks_beyond_current);
150150
}
151151

152-
/// Returns the raw context pointer for the C API.
153-
wasmtime_context_t *raw_context() { return ptr; }
152+
/// \brief Returns the underlying C API pointer.
153+
const wasmtime_context_t *capi() const { return ptr; }
154+
155+
/// \brief Returns the underlying C API pointer.
156+
wasmtime_context_t *capi() { return ptr; }
154157
};
155158

156159
/// \brief Provides limits for a store. Used by hosts to limit resource
@@ -195,6 +198,12 @@ public:
195198
/// Runs a garbage collection pass in the referenced store to collect loose
196199
/// GC-managed objects, if any are available.
197200
void gc() { context().gc(); }
201+
202+
/// \brief Returns the underlying C API pointer.
203+
const wasmtime_store_t *capi() const { return ptr.get(); }
204+
205+
/// \brief Returns the underlying C API pointer.
206+
wasmtime_store_t *capi() { return ptr.get(); }
198207
};
199208

200209
} // namespace wasmtime

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,14 @@ public:
8888
/// Consumes ownership of the underlying `wasmtime_externref_t` and returns
8989
/// the result of `wasmtime_externref_to_raw`.
9090
uint32_t take_raw(Store::Context cx) {
91-
uint32_t ret = wasmtime_externref_to_raw(cx.raw_context(), &val);
91+
uint32_t ret = wasmtime_externref_to_raw(cx.capi(), &val);
9292
wasmtime_externref_set_null(&val);
9393
return ret;
9494
}
9595

9696
/// Returns `wasmtime_externref_to_raw`.
9797
uint32_t borrow_raw(Store::Context cx) const {
98-
return wasmtime_externref_to_raw(cx.raw_context(), &val);
98+
return wasmtime_externref_to_raw(cx.capi(), &val);
9999
}
100100
};
101101

@@ -148,14 +148,14 @@ public:
148148
/// Consumes ownership of the underlying `wasmtime_anyref_t` and returns the
149149
/// result of `wasmtime_anyref_to_raw`.
150150
uint32_t take_raw(Store::Context cx) {
151-
uint32_t ret = wasmtime_anyref_to_raw(cx.raw_context(), &val);
151+
uint32_t ret = wasmtime_anyref_to_raw(cx.capi(), &val);
152152
wasmtime_anyref_set_null(&val);
153153
return ret;
154154
}
155155

156156
/// Returns `wasmtime_anyref_to_raw`.
157157
uint32_t borrow_raw(Store::Context cx) const {
158-
return wasmtime_anyref_to_raw(cx.raw_context(), &val);
158+
return wasmtime_anyref_to_raw(cx.capi(), &val);
159159
}
160160

161161
/// \brief If this is an `i31`, get the value zero-extended.

crates/c-api/src/component/component.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,15 @@ pub unsafe extern "C" fn wasmtime_component_get_export_index(
115115
.map(|export_index| Box::new(wasmtime_component_export_index_t { export_index }))
116116
}
117117

118+
#[unsafe(no_mangle)]
119+
pub unsafe extern "C" fn wasmtime_component_export_index_clone(
120+
export_index: &wasmtime_component_export_index_t,
121+
) -> Box<wasmtime_component_export_index_t> {
122+
Box::new(wasmtime_component_export_index_t {
123+
export_index: export_index.export_index,
124+
})
125+
}
126+
118127
#[unsafe(no_mangle)]
119128
pub unsafe extern "C" fn wasmtime_component_export_index_delete(
120129
_export_index: Box<wasmtime_component_export_index_t>,

0 commit comments

Comments
 (0)