1 /**
2  * \file wasmtime/store.hh
3  */
4 
5 #ifndef WASMTIME_STORE_HH
6 #define WASMTIME_STORE_HH
7 
8 #include <any>
9 #include <memory>
10 #include <optional>
11 #include <wasmtime/conf.h>
12 #include <wasmtime/engine.hh>
13 #include <wasmtime/error.hh>
14 #include <wasmtime/helpers.hh>
15 #include <wasmtime/store.h>
16 #include <wasmtime/wasi.hh>
17 
18 namespace wasmtime {
19 
20 class Caller;
21 class Tag;
22 class Exn;
23 class Trap;
24 
25 /// \brief An enum for the behavior before extending the epoch deadline.
26 enum class DeadlineKind {
27   /// Directly continue to updating the deadline and executing WebAssembly.
28   Continue = WASMTIME_UPDATE_DEADLINE_CONTINUE,
29   /// Yield control (via async support) then update the deadline.
30   Yield = WASMTIME_UPDATE_DEADLINE_YIELD,
31 };
32 
33 /**
34  * \brief Owner of all WebAssembly objects
35  *
36  * A `Store` owns all WebAssembly objects such as instances, globals, functions,
37  * memories, etc. A `Store` is one of the main central points about working with
38  * WebAssembly since it's an argument to almost all APIs. The `Store` serves as
39  * a form of "context" to give meaning to the pointers of `Func` and friends.
40  *
41  * A `Store` can be sent between threads but it cannot generally be shared
42  * concurrently between threads. Memory associated with WebAssembly instances
43  * will be deallocated when the `Store` is deallocated.
44  */
45 class Store {
46   WASMTIME_OWN_WRAPPER(Store, wasmtime_store);
47 
48 private:
finalizer(void * ptr)49   static void finalizer(void *ptr) {
50     std::unique_ptr<std::any> _ptr(static_cast<std::any *>(ptr));
51   }
52 
53 public:
54   /// Creates a new `Store` within the provided `Engine`.
Store(Engine & engine)55   explicit Store(Engine &engine)
56       : ptr(wasmtime_store_new(engine.capi(), nullptr, finalizer)) {}
57 
58   /**
59    * \brief An interior pointer into a `Store`.
60    *
61    * A `Context` object is created from either a `Store` or a `Caller`. It is an
62    * interior pointer into a `Store` and cannot be used outside the lifetime of
63    * the original object it was created from.
64    *
65    * This object is an argument to most APIs in Wasmtime but typically doesn't
66    * need to be constructed explicitly since it can be created from a `Store&`
67    * or a `Caller&`.
68    */
69   class Context {
70     friend class Global;
71     friend class Table;
72     friend class Memory;
73     friend class Func;
74     friend class Instance;
75     friend class Linker;
76     friend class ExternRef;
77     friend class AnyRef;
78     friend class EqRef;
79     friend class Val;
80     friend class Store;
81     friend class Tag;
82     wasmtime_context_t *ptr;
83 
84   public:
85     /// Creates a context from the raw C API pointer.
Context(wasmtime_context_t * ptr)86     explicit Context(wasmtime_context_t *ptr) : ptr(ptr) {}
87 
88     /// Creates a context referencing the provided `Store`.
Context(Store & store)89     Context(Store &store) : Context(wasmtime_store_context(store.ptr.get())) {}
90     /// Creates a context referencing the provided `Store`.
Context(Store * store)91     Context(Store *store) : Context(*store) {}
92     /// Creates a context referencing the provided `Caller`.
93     Context(Caller &caller);
94     /// Creates a context referencing the provided `Caller`.
95     Context(Caller *caller);
96 
97     /// Runs a garbage collection pass in the referenced store to collect loose
98     /// `externref` values, if any are available.
gc()99     Result<std::monostate> gc() {
100       auto *error = wasmtime_context_gc(ptr);
101       if (error != nullptr) {
102         return Error(error);
103       }
104       return std::monostate();
105     }
106 
107     /// Injects fuel to be consumed within this store.
108     ///
109     /// Stores start with 0 fuel and if `Config::consume_fuel` is enabled then
110     /// this is required if you want to let WebAssembly actually execute.
111     ///
112     /// Returns an error if fuel consumption isn't enabled.
set_fuel(uint64_t fuel)113     Result<std::monostate> set_fuel(uint64_t fuel) {
114       auto *error = wasmtime_context_set_fuel(ptr, fuel);
115       if (error != nullptr) {
116         return Error(error);
117       }
118       return std::monostate();
119     }
120 
121     /// Returns the amount of fuel consumed so far by executing WebAssembly.
122     ///
123     /// Returns `std::nullopt` if fuel consumption is not enabled.
get_fuel() const124     Result<uint64_t> get_fuel() const {
125       uint64_t fuel = 0;
126       auto *error = wasmtime_context_get_fuel(ptr, &fuel);
127       if (error != nullptr) {
128         return Error(error);
129       }
130       return fuel;
131     }
132 
133     /// Set user specified data associated with this store.
set_data(std::any data) const134     void set_data(std::any data) const {
135       finalizer(static_cast<std::any *>(wasmtime_context_get_data(ptr)));
136       wasmtime_context_set_data(
137           ptr, std::make_unique<std::any>(std::move(data)).release());
138     }
139 
140     /// Get user specified data associated with this store.
get_data() const141     std::any &get_data() const {
142       return *static_cast<std::any *>(wasmtime_context_get_data(ptr));
143     }
144 
145 #ifdef WASMTIME_FEATURE_WASI
146     /// Configures the WASI state used by this store.
147     ///
148     /// This will only have an effect if used in conjunction with
149     /// `Linker::define_wasi` because otherwise no host functions will use the
150     /// WASI state.
set_wasi(WasiConfig config)151     Result<std::monostate> set_wasi(WasiConfig config) {
152       auto *error = wasmtime_context_set_wasi(ptr, config.capi_release());
153       if (error != nullptr) {
154         return Error(error);
155       }
156       return std::monostate();
157     }
158 #endif // WASMTIME_FEATURE_WASI
159 
160     /// Configures this store's epoch deadline to be the specified number of
161     /// ticks beyond the engine's current epoch.
162     ///
163     /// By default the deadline is the current engine's epoch, immediately
164     /// interrupting code if epoch interruption is enabled. This must be called
165     /// to extend the deadline to allow interruption.
set_epoch_deadline(uint64_t ticks_beyond_current)166     void set_epoch_deadline(uint64_t ticks_beyond_current) {
167       wasmtime_context_set_epoch_deadline(ptr, ticks_beyond_current);
168     }
169 
170 #ifdef WASMTIME_FEATURE_GC
171     /// \brief Sets the pending exception on the store and returns a Trap.
172     ///
173     /// This transfers ownership of `exn`. After this call, `exn` is consumed.
174     /// Returns a Trap that the host callback MUST return to propagate the
175     /// exception through Wasm catch blocks.
176     inline Trap throw_exception(Exn exn);
177 
178     /// \brief Takes the pending exception from the store, if any.
179     ///
180     /// Returns the exception if one was pending, or std::nullopt.
181     inline std::optional<Exn> take_exception();
182 
183     /// \brief Tests whether there is a pending exception on the store.
184     inline bool has_exception();
185 #endif // WASMTIME_FEATURE_GC
186 
187     /// \brief Returns the underlying C API pointer.
capi() const188     const wasmtime_context_t *capi() const { return ptr; }
189 
190     /// \brief Returns the underlying C API pointer.
capi()191     wasmtime_context_t *capi() { return ptr; }
192   };
193 
194   /// \brief Provides limits for a store. Used by hosts to limit resource
195   /// consumption of instances. Use negative value to keep the default value
196   /// for the limit.
197   ///
198   /// \param memory_size the maximum number of bytes a linear memory can grow
199   /// to. Growing a linear memory beyond this limit will fail. By default,
200   /// linear memory will not be limited.
201   ///
202   /// \param table_elements the maximum number of elements in a table.
203   /// Growing a table beyond this limit will fail. By default, table elements
204   /// will not be limited.
205   ///
206   /// \param instances the maximum number of instances that can be created
207   /// for a Store. Module instantiation will fail if this limit is exceeded.
208   /// This value defaults to 10,000.
209   ///
210   /// \param tables the maximum number of tables that can be created for a
211   /// Store. Module instantiation will fail if this limit is exceeded. This
212   /// value defaults to 10,000.
213   ///
214   /// \param memories the maximum number of linear
215   /// memories that can be created for a Store. Instantiation will fail with an
216   /// error if this limit is exceeded. This value defaults to 10,000.
217   ///
218   /// Use any negative value for the parameters that should be kept on
219   /// the default values.
220   ///
221   /// Note that the limits are only used to limit the creation/growth of
222   /// resources in the future, this does not retroactively attempt to apply
223   /// limits to the store.
limiter(int64_t memory_size,int64_t table_elements,int64_t instances,int64_t tables,int64_t memories)224   void limiter(int64_t memory_size, int64_t table_elements, int64_t instances,
225                int64_t tables, int64_t memories) {
226     wasmtime_store_limiter(ptr.get(), memory_size, table_elements, instances,
227                            tables, memories);
228   }
229 
230   /// \brief Configures epoch deadline callback to C function.
231   ///
232   /// This function configures a store-local callback function that will be
233   /// called when the running WebAssembly function has exceeded its epoch
234   /// deadline. That function can:
235   /// - return an error to terminate the function
236   /// - set the delta argument and return DeadlineKind::Continue to update the
237   ///   epoch deadline delta and resume function execution.
238   /// - set the delta argument, update the epoch deadline, and return
239   ///   DeadlineKind::Yield to yield (via async support) and resume function
240   ///   execution.
241   template <typename F,
242             std::enable_if_t<std::is_invocable_r_v<Result<DeadlineKind>, F,
243                                                    Context, uint64_t &>,
244                              bool> = true>
epoch_deadline_callback(F && f)245   void epoch_deadline_callback(F &&f) {
246     wasmtime_store_epoch_deadline_callback(
247         ptr.get(), raw_epoch_callback<std::remove_reference_t<F>>,
248         std::make_unique<std::remove_reference_t<F>>(std::forward<F>(f))
249             .release(),
250         raw_epoch_finalizer<std::remove_reference_t<F>>);
251   }
252 
253   /// Explicit function to acquire a `Context` from this store.
context()254   Context context() { return this; }
255 
256   /// Runs a garbage collection pass in the referenced store to collect loose
257   /// GC-managed objects, if any are available.
gc()258   Result<std::monostate> gc() { return context().gc(); }
259 
260 private:
261   template <typename F>
262   static wasmtime_error_t *
raw_epoch_callback(wasmtime_context_t * context,void * data,uint64_t * epoch_deadline_delta,wasmtime_update_deadline_kind_t * update_kind)263   raw_epoch_callback(wasmtime_context_t *context, void *data,
264                      uint64_t *epoch_deadline_delta,
265                      wasmtime_update_deadline_kind_t *update_kind) {
266     auto &callback = *static_cast<F *>(data);
267     Context ctx(context);
268     auto result = callback(ctx, *epoch_deadline_delta);
269 
270     if (!result) {
271       return result.err().capi_release();
272     }
273     *update_kind = static_cast<wasmtime_update_deadline_kind_t>(result.ok());
274     return nullptr;
275   }
276 
raw_epoch_finalizer(void * data)277   template <typename F> static void raw_epoch_finalizer(void *data) {
278     std::unique_ptr<F> _ptr(static_cast<F *>(data));
279   }
280 };
281 
282 } // namespace wasmtime
283 
284 #endif // WASMTIME_STORE_HH
285