wasmer_c_api/wasm_c_api/unstable/middlewares/
metering.rs

1//! Unstable non-standard Wasmer-specific API that contains everything
2//! to create the middleware metering API.
3//!
4//! The metering middleware is used for tracking how many operators
5//! are executed in total and putting a limit on the total number of
6//! operators executed.
7//!
8//! # Example
9//!
10//! ```rust
11//! # use wasmer_inline_c::assert_c;
12//! # fn main() {
13//! #    (assert_c! {
14//! # #include "tests/wasmer.h"
15//! #
16//! // Define our “cost function”.
17//! uint64_t cost_function(wasmer_parser_operator_t wasm_operator) {
18//!     switch(wasm_operator) {
19//!         // `local.get` and `i32.const` cost 1 unit.
20//!         case LocalGet:
21//!         case I32Const:
22//!             return 1;
23//!
24//!         // `i32.add` costs 2 units.
25//!         case I32Add:
26//!             return 2;
27//!
28//!         // The other operations are free.
29//!         default:
30//!             return 0;
31//!     }
32//! }
33//!
34//! int main() {
35//!     // Create a new metering middleware, with our cost function.
36//!     wasmer_metering_t* metering = wasmer_metering_new(10, cost_function);
37//!
38//!     // Consume `metering` to produce a generic `wasmer_middleware_t` value.
39//!     wasmer_middleware_t* middleware = wasmer_metering_as_middleware(metering);
40//!     
41//!     // Create a new configuration, and push the middleware in it.
42//!     wasm_config_t* config = wasm_config_new();
43//!     wasm_config_push_middleware(config, middleware);
44//!     
45//!     // Create the engine and the store based on the configuration.
46//!     wasm_engine_t* engine = wasm_engine_new_with_config(config);
47//!     wasm_store_t* store = wasm_store_new(engine);
48//!     
49//!     // Create the new WebAssembly module.
50//!     wasm_byte_vec_t wat;
51//!     wasmer_byte_vec_new_from_string(
52//!         &wat,
53//!         "(module\n"
54//!         "  (type $add_t (func (param i32) (result i32)))\n"
55//!         "  (func $add_two_f (type $add_t) (param $value i32) (result i32)\n"
56//!         "    local.get $value\n"
57//!         "    i32.const 1\n"
58//!         "    i32.add)\n"
59//!         "  (export \"add_two\" (func $add_two_f)))"
60//!     );
61//!     wasm_byte_vec_t wasm;
62//!     wat2wasm(&wat, &wasm);
63//!
64//!     wasm_module_t* module = wasm_module_new(store, &wasm);
65//!     assert(module);
66//!     
67//!     // Instantiate the module.
68//!     wasm_extern_vec_t imports = WASM_EMPTY_VEC;
69//!     wasm_trap_t* trap = NULL;
70//!     wasm_instance_t* instance = wasm_instance_new(store, module, &imports, &trap);
71//!     assert(instance);
72//!     
73//!     // Here we go. At this step, we will get the `add_two` exported function, and
74//!     // call it.
75//!     wasm_extern_vec_t exports;
76//!     wasm_instance_exports(instance, &exports);
77//!     assert(exports.size >= 1);
78//!     assert(wasm_extern_kind(exports.data[0]) == WASM_EXTERN_FUNC);
79//!
80//!     const wasm_func_t* add_two = wasm_extern_as_func(exports.data[0]);
81//!     assert(add_two);
82//!
83//!     wasm_val_t arguments[1] = { WASM_I32_VAL(41) };
84//!     wasm_val_t results[1] = { WASM_INIT_VAL };
85//!
86//!     wasm_val_vec_t arguments_as_array = WASM_ARRAY_VEC(arguments);
87//!     wasm_val_vec_t results_as_array = WASM_ARRAY_VEC(results);
88//!
89//!     // Let's call `add_two` for the first time!
90//!     {
91//!         trap = wasm_func_call(add_two, &arguments_as_array, &results_as_array);
92//!         assert(trap == NULL);
93//!         assert(results[0].of.i32 == 42);
94//!
95//!         // There is 6 points left!
96//!         assert(wasmer_metering_get_remaining_points(instance) == 6);
97//!         assert(wasmer_metering_points_are_exhausted(instance) == false);
98//!     }
99//!
100//!     // Let's call `add_two` for the second time!
101//!     {
102//!         trap = wasm_func_call(add_two, &arguments_as_array, &results_as_array);
103//!         assert(trap == NULL);
104//!         assert(results[0].of.i32 == 42);
105//!
106//!         // There is 2 points left!
107//!         assert(wasmer_metering_get_remaining_points(instance) == 2);
108//!         assert(wasmer_metering_points_are_exhausted(instance) == false);
109//!     }
110//!
111//!     // Let's call `add_two` for the third time!
112//!     {
113//!         trap = wasm_func_call(add_two, &arguments_as_array, &results_as_array);
114//!         // Oh, it failed!
115//!         assert(trap != NULL);
116//!
117//!         // There is 0 point left… they are exhausted.
118//!         assert(wasmer_metering_points_are_exhausted(instance) == true);
119//!     }
120//!
121//!     wasm_extern_vec_delete(&exports);
122//!     wasm_instance_delete(instance);
123//!     wasm_module_delete(module);
124//!     wasm_store_delete(store);
125//!     wasm_engine_delete(engine);
126//!
127//!     return 0;
128//! }
129//! #    })
130//! #    .success();
131//! # }
132//! ```
133
134use super::super::super::instance::wasm_instance_t;
135use super::super::parser::operator::wasmer_parser_operator_t;
136use super::wasmer_middleware_t;
137use std::sync::Arc;
138use wasmer_api::wasmparser::Operator;
139use wasmer_middlewares::{
140    Metering,
141    metering::{MeteringPoints, get_remaining_points, set_remaining_points},
142};
143
144/// Opaque type representing a metering middleware.
145///
146/// To transform this specific middleware into a generic one, please
147/// see [`wasmer_metering_as_middleware`].
148///
149/// # Example
150///
151/// See module's documentation.
152#[allow(non_camel_case_types, clippy::type_complexity)]
153pub struct wasmer_metering_t {
154    pub(crate) inner: Arc<Metering<Box<dyn Fn(&Operator) -> u64 + Send + Sync>>>,
155}
156
157/// Function type to represent a user-defined cost function
158/// implemented in C.
159///
160/// # Example
161///
162/// See module's documentation.
163#[allow(non_camel_case_types)]
164pub type wasmer_metering_cost_function_t =
165    extern "C" fn(wasm_operator: wasmer_parser_operator_t) -> u64;
166
167/// Creates a new metering middleware with an initial limit, i.e. a
168/// total number of operators to execute (regarding their respective
169/// cost), in addition to a cost function. The cost function defines
170/// the cost of an operation, that will decrease the initial limit.
171///
172/// # Example
173///
174/// See module's documentation.
175#[unsafe(no_mangle)]
176pub extern "C" fn wasmer_metering_new(
177    initial_limit: u64,
178    cost_function: wasmer_metering_cost_function_t,
179) -> Box<wasmer_metering_t> {
180    let cost_function = move |operator: &Operator| -> u64 { cost_function(operator.into()) };
181
182    Box::new(wasmer_metering_t {
183        inner: Arc::new(Metering::new(initial_limit, Box::new(cost_function))),
184    })
185}
186
187/// Deletes a [`wasmer_metering_t`].
188///
189/// # Example
190///
191/// See module's documentation.
192#[unsafe(no_mangle)]
193pub extern "C" fn wasmer_metering_delete(_metering: Option<Box<wasmer_metering_t>>) {}
194
195/// Returns the remaining metering points. `u64::MAX` means
196/// points are exhausted, otherwise it returns the number of
197/// points. Notice that it could include zero! Zero doesn't mean
198/// points are exhausted _yet_.
199///
200/// # Example
201///
202/// See module's documentation.
203#[unsafe(no_mangle)]
204pub unsafe extern "C" fn wasmer_metering_get_remaining_points(
205    instance: &mut wasm_instance_t,
206) -> u64 {
207    let mut store_mut = unsafe { instance.store.store_mut() };
208    match get_remaining_points(&mut store_mut, &instance.inner) {
209        MeteringPoints::Remaining(value) => value,
210        MeteringPoints::Exhausted => u64::MAX,
211    }
212}
213
214/// Returns true if the remaning points are exhausted, false otherwise.
215///
216/// # Example
217///
218/// See module's documentation.
219#[unsafe(no_mangle)]
220pub unsafe extern "C" fn wasmer_metering_points_are_exhausted(
221    instance: &mut wasm_instance_t,
222) -> bool {
223    let mut store_mut = unsafe { instance.store.store_mut() };
224    matches!(
225        get_remaining_points(&mut store_mut, &instance.inner),
226        MeteringPoints::Exhausted,
227    )
228}
229
230/// Set a new amount of points for the given metering middleware.
231///
232/// # Example
233///
234/// This example only illustrates the
235/// `wasmer_metering_set_remaining_points` function, as the number of
236/// points aren't updated by the WebAssembly module execution
237///
238/// ```rust
239/// # use wasmer_inline_c::assert_c;
240/// # fn main() {
241/// #    (assert_c! {
242/// # #include "tests/wasmer.h"
243/// #
244/// // Define a dummy “cost function”.
245/// uint64_t cost_function(wasmer_parser_operator_t wasm_operator) {
246///     switch(wasm_operator) {
247///         default:
248///             return 0;
249///     }
250/// }
251///
252/// int main() {
253///     // Set the initial amount of points to 7.
254///     wasmer_metering_t* metering = wasmer_metering_new(7, cost_function);
255///
256///     // Consume `metering` to produce `middleware`.
257///     wasmer_middleware_t* middleware = wasmer_metering_as_middleware(metering);
258///
259///     // Create the configuration (which consumes `middleware`),
260///     // the engine, and the store.
261///     wasm_config_t* config = wasm_config_new();
262///     wasm_config_push_middleware(config, middleware);
263///     
264///     wasm_engine_t* engine = wasm_engine_new_with_config(config);
265///
266///     wasm_store_t* store = wasm_store_new(engine);
267///     
268///     // Create the module and instantiate it.
269///     wasm_byte_vec_t wat;
270///     wasmer_byte_vec_new_from_string(&wat, "(module)");
271///     wasm_byte_vec_t wasm;
272///     wat2wasm(&wat, &wasm);
273///
274///     wasm_module_t* module = wasm_module_new(store, &wasm);
275///     assert(module);
276///     
277///     wasm_extern_vec_t imports = WASM_EMPTY_VEC;
278///     wasm_trap_t* trap = NULL;
279///     wasm_instance_t* instance = wasm_instance_new(store, module, &imports, &trap);
280///     assert(instance);
281///
282///     // Read the number of points.
283///     assert(wasmer_metering_get_remaining_points(instance) == 7);
284///
285///     // Set a new number of points.
286///     wasmer_metering_set_remaining_points(instance, 42);
287///
288///     // Read the number of points.
289///     assert(wasmer_metering_get_remaining_points(instance) == 42);
290///
291///     wasm_instance_delete(instance);
292///     wasm_module_delete(module);
293///     wasm_store_delete(store);
294///     wasm_engine_delete(engine);
295///
296///     return 0;
297/// }
298/// #    })
299/// #    .success();
300/// # }
301/// ```
302#[unsafe(no_mangle)]
303pub unsafe extern "C" fn wasmer_metering_set_remaining_points(
304    instance: &mut wasm_instance_t,
305    new_limit: u64,
306) {
307    let mut store_mut = unsafe { instance.store.store_mut() };
308    set_remaining_points(&mut store_mut, &instance.inner, new_limit);
309}
310
311/// Transforms a [`wasmer_metering_t`] into a generic
312/// [`wasmer_middleware_t`], to then be pushed in the configuration with
313/// [`wasm_config_push_middleware`](crate::wasm_c_api::engine::wasm_config_push_middleware).
314///
315/// This function takes ownership of `metering`.
316///
317/// # Example
318///
319/// See module's documentation.
320#[unsafe(no_mangle)]
321pub extern "C" fn wasmer_metering_as_middleware(
322    metering: Option<Box<wasmer_metering_t>>,
323) -> Option<Box<wasmer_middleware_t>> {
324    let metering = metering?;
325
326    Some(Box::new(wasmer_middleware_t {
327        inner: metering.inner,
328    }))
329}