1 /**
2  * \file wasmtime/val.hh
3  */
4 
5 #ifndef WASMTIME_VAL_HH
6 #define WASMTIME_VAL_HH
7 
8 #include <optional>
9 #include <wasmtime/store.hh>
10 #include <wasmtime/types/val.hh>
11 #include <wasmtime/val.h>
12 
13 namespace wasmtime {
14 
15 /**
16  * \brief Representation of a WebAssembly `externref` value.
17  *
18  * This class represents an value that cannot be forged by WebAssembly itself.
19  * All `ExternRef` values are guaranteed to be created by the host and its
20  * embedding. It's suitable to place private data structures in here which
21  * WebAssembly will not have access to, only other host functions will have
22  * access to them.
23  *
24  * Note that `ExternRef` values are rooted within a `Store` and must be manually
25  * unrooted via the `unroot` function. If this is not used then values will
26  * never be candidates for garbage collection.
27  */
28 class ExternRef {
29   friend class Val;
30 
31   wasmtime_externref_t val;
32 
33   static void finalizer(void *ptr) {
34     std::unique_ptr<std::any> _ptr(static_cast<std::any *>(ptr));
35   }
36 
37 public:
38   /// Creates a new `ExternRef` directly from its C-API representation.
39   explicit ExternRef(wasmtime_externref_t val) : val(val) {}
40 
41   /// Creates a new `externref` value from the provided argument.
42   ///
43   /// Note that `val` should be safe to send across threads and should own any
44   /// memory that it points to. Also note that `ExternRef` is similar to a
45   /// `std::shared_ptr` in that there can be many references to the same value.
46   template <typename T> explicit ExternRef(Store::Context cx, T val) {
47     void *ptr = std::make_unique<std::any>(std::move(val)).release();
48     bool ok = wasmtime_externref_new(cx.ptr, ptr, finalizer, &this->val);
49     if (!ok) {
50       fprintf(stderr, "failed to allocate a new externref");
51       abort();
52     }
53   }
54 
55   /// Creates a new `ExternRef` which is separately rooted from this one.
56   ExternRef clone(Store::Context cx) {
57     wasmtime_externref_t other;
58     wasmtime_externref_clone(cx.ptr, &val, &other);
59     return ExternRef(other);
60   }
61 
62   /// Returns the underlying host data associated with this `ExternRef`.
63   std::any &data(Store::Context cx) {
64     return *static_cast<std::any *>(wasmtime_externref_data(cx.ptr, &val));
65   }
66 
67   /// Unroots this value from the context provided, enabling a future GC to
68   /// collect the internal object if there are no more references.
69   void unroot(Store::Context cx) { wasmtime_externref_unroot(cx.ptr, &val); }
70 
71   /// Returns the raw underlying C API value.
72   ///
73   /// This class still retains ownership of the pointer.
74   const wasmtime_externref_t *raw() const { return &val; }
75 };
76 
77 /**
78  * \brief Representation of a WebAssembly `anyref` value.
79  */
80 class AnyRef {
81   friend class Val;
82 
83   wasmtime_anyref_t val;
84 
85 public:
86   /// Creates a new `AnyRef` directly from its C-API representation.
87   explicit AnyRef(wasmtime_anyref_t val) : val(val) {}
88 
89   /// Creates a new `AnyRef` which is an `i31` with the given `value`,
90   /// truncated if the upper bit is set.
91   static AnyRef i31(Store::Context cx, uint32_t value) {
92     wasmtime_anyref_t other;
93     wasmtime_anyref_from_i31(cx.ptr, value, &other);
94     return AnyRef(other);
95   }
96 
97   /// Creates a new `AnyRef` which is separately rooted from this one.
98   AnyRef clone(Store::Context cx) {
99     wasmtime_anyref_t other;
100     wasmtime_anyref_clone(cx.ptr, &val, &other);
101     return AnyRef(other);
102   }
103 
104   /// Unroots this value from the context provided, enabling a future GC to
105   /// collect the internal object if there are no more references.
106   void unroot(Store::Context cx) { wasmtime_anyref_unroot(cx.ptr, &val); }
107 
108   /// Returns the raw underlying C API value.
109   ///
110   /// This class still retains ownership of the pointer.
111   const wasmtime_anyref_t *raw() const { return &val; }
112 
113   /// \brief If this is an `i31`, get the value zero-extended.
114   std::optional<uint32_t> u31(Store::Context cx) const {
115     uint32_t ret = 0;
116     if (wasmtime_anyref_i31_get_u(cx.ptr, &val, &ret))
117       return ret;
118     return std::nullopt;
119   }
120 
121   /// \brief If this is an `i31`, get the value sign-extended.
122   std::optional<int32_t> i31(Store::Context cx) const {
123     int32_t ret = 0;
124     if (wasmtime_anyref_i31_get_s(cx.ptr, &val, &ret))
125       return ret;
126     return std::nullopt;
127   }
128 };
129 
130 /// \brief Container for the `v128` WebAssembly type.
131 struct V128 {
132   /// \brief The little-endian bytes of the `v128` value.
133   wasmtime_v128 v128;
134 
135   /// \brief Creates a new zero-value `v128`.
136   V128() : v128{} { memset(&v128[0], 0, sizeof(wasmtime_v128)); }
137 
138   /// \brief Creates a new `V128` from its C API representation.
139   V128(const wasmtime_v128 &v) : v128{} {
140     memcpy(&v128[0], &v[0], sizeof(wasmtime_v128));
141   }
142 };
143 
144 class Func;
145 
146 /**
147  * \brief Representation of a generic WebAssembly value.
148  *
149  * This is roughly equivalent to a tagged union of all possible WebAssembly
150  * values. This is later used as an argument with functions, globals, tables,
151  * etc.
152  *
153  * Note that a `Val` can represent owned GC pointers. In this case the `unroot`
154  * method must be used to ensure that they can later be garbage-collected.
155  */
156 class Val {
157   friend class Global;
158   friend class Table;
159   friend class Func;
160 
161   wasmtime_val_t val;
162 
163   Val() : val{} {
164     val.kind = WASMTIME_I32;
165     val.of.i32 = 0;
166   }
167   Val(wasmtime_val_t val) : val(val) {}
168 
169 public:
170   /// Creates a new `i32` WebAssembly value.
171   Val(int32_t i32) : val{} {
172     val.kind = WASMTIME_I32;
173     val.of.i32 = i32;
174   }
175   /// Creates a new `i64` WebAssembly value.
176   Val(int64_t i64) : val{} {
177     val.kind = WASMTIME_I64;
178     val.of.i64 = i64;
179   }
180   /// Creates a new `f32` WebAssembly value.
181   Val(float f32) : val{} {
182     val.kind = WASMTIME_F32;
183     val.of.f32 = f32;
184   }
185   /// Creates a new `f64` WebAssembly value.
186   Val(double f64) : val{} {
187     val.kind = WASMTIME_F64;
188     val.of.f64 = f64;
189   }
190   /// Creates a new `v128` WebAssembly value.
191   Val(const V128 &v128) : val{} {
192     val.kind = WASMTIME_V128;
193     memcpy(&val.of.v128[0], &v128.v128[0], sizeof(wasmtime_v128));
194   }
195   /// Creates a new `funcref` WebAssembly value.
196   Val(std::optional<Func> func);
197   /// Creates a new `funcref` WebAssembly value which is not `ref.null func`.
198   Val(Func func);
199   /// Creates a new `externref` value.
200   Val(std::optional<ExternRef> ptr) : val{} {
201     val.kind = WASMTIME_EXTERNREF;
202     if (ptr) {
203       val.of.externref = ptr->val;
204     } else {
205       wasmtime_externref_set_null(&val.of.externref);
206     }
207   }
208   /// Creates a new `anyref` value.
209   Val(std::optional<AnyRef> ptr) : val{} {
210     val.kind = WASMTIME_ANYREF;
211     if (ptr) {
212       val.of.anyref = ptr->val;
213     } else {
214       wasmtime_anyref_set_null(&val.of.anyref);
215     }
216   }
217   /// Creates a new `externref` WebAssembly value which is not `ref.null
218   /// extern`.
219   Val(ExternRef ptr);
220   /// Creates a new `anyref` WebAssembly value which is not `ref.null
221   /// any`.
222   Val(AnyRef ptr);
223 
224   /// Returns the kind of value that this value has.
225   ValKind kind() const {
226     switch (val.kind) {
227     case WASMTIME_I32:
228       return ValKind::I32;
229     case WASMTIME_I64:
230       return ValKind::I64;
231     case WASMTIME_F32:
232       return ValKind::F32;
233     case WASMTIME_F64:
234       return ValKind::F64;
235     case WASMTIME_FUNCREF:
236       return ValKind::FuncRef;
237     case WASMTIME_EXTERNREF:
238       return ValKind::ExternRef;
239     case WASMTIME_ANYREF:
240       return ValKind::AnyRef;
241     case WASMTIME_V128:
242       return ValKind::V128;
243     }
244     std::abort();
245   }
246 
247   /// Returns the underlying `i32`, requires `kind() == KindI32` or aborts the
248   /// process.
249   int32_t i32() const {
250     if (val.kind != WASMTIME_I32) {
251       std::abort();
252     }
253     return val.of.i32;
254   }
255 
256   /// Returns the underlying `i64`, requires `kind() == KindI64` or aborts the
257   /// process.
258   int64_t i64() const {
259     if (val.kind != WASMTIME_I64) {
260       std::abort();
261     }
262     return val.of.i64;
263   }
264 
265   /// Returns the underlying `f32`, requires `kind() == KindF32` or aborts the
266   /// process.
267   float f32() const {
268     if (val.kind != WASMTIME_F32) {
269       std::abort();
270     }
271     return val.of.f32;
272   }
273 
274   /// Returns the underlying `f64`, requires `kind() == KindF64` or aborts the
275   /// process.
276   double f64() const {
277     if (val.kind != WASMTIME_F64) {
278       std::abort();
279     }
280     return val.of.f64;
281   }
282 
283   /// Returns the underlying `v128`, requires `kind() == KindV128` or aborts
284   /// the process.
285   V128 v128() const {
286     if (val.kind != WASMTIME_V128) {
287       std::abort();
288     }
289     return val.of.v128;
290   }
291 
292   /// Returns the underlying `externref`, requires `kind() == KindExternRef` or
293   /// aborts the process.
294   ///
295   /// Note that `externref` is a nullable reference, hence the `optional` return
296   /// value.
297   std::optional<ExternRef> externref(Store::Context cx) const {
298     if (val.kind != WASMTIME_EXTERNREF) {
299       std::abort();
300     }
301     if (wasmtime_externref_is_null(&val.of.externref)) {
302       return std::nullopt;
303     }
304     wasmtime_externref_t other;
305     wasmtime_externref_clone(cx.ptr, &val.of.externref, &other);
306     return ExternRef(other);
307   }
308 
309   /// Returns the underlying `anyref`, requires `kind() == KindAnyRef` or
310   /// aborts the process.
311   ///
312   /// Note that `anyref` is a nullable reference, hence the `optional` return
313   /// value.
314   std::optional<AnyRef> anyref(Store::Context cx) const {
315     if (val.kind != WASMTIME_ANYREF) {
316       std::abort();
317     }
318     if (wasmtime_anyref_is_null(&val.of.anyref)) {
319       return std::nullopt;
320     }
321     wasmtime_anyref_t other;
322     wasmtime_anyref_clone(cx.ptr, &val.of.anyref, &other);
323     return AnyRef(other);
324   }
325 
326   /// Returns the underlying `funcref`, requires `kind() == KindFuncRef` or
327   /// aborts the process.
328   ///
329   /// Note that `funcref` is a nullable reference, hence the `optional` return
330   /// value.
331   std::optional<Func> funcref() const;
332 
333   /// Unroots any GC references this `Val` points to within the `cx` provided.
334   void unroot(Store::Context cx) { wasmtime_val_unroot(cx.ptr, &val); }
335 };
336 
337 } // namespace wasmtime
338 
339 // fill in `Func` constructors for `Val`
340 #include <wasmtime/func.hh>
341 
342 #endif // WASMTIME_VAL_HH
343