wasmer_c_api/
error.rs

1//! Utilities to read errors.
2//!
3//! Only one error can be registered at a time. Error are registered
4//! by Rust only, and are usually read by C or C++.
5//!
6//! Reading an error from C or C++ happens in 2 steps: Getting the
7//! error's length with [`wasmer_last_error_length`], and then reading
8//! the actual error with [`wasmer_last_error_message`].
9//!
10//! # Example
11//!
12//! ```rust
13//! # use wasmer_inline_c::assert_c;
14//! # fn main() {
15//! #    (assert_c! {
16//! # #include "tests/wasmer.h"
17//! #
18//! int main() {
19//!     // Create an invalid WebAssembly module from a WAT definition,
20//!     // it will generate an error!
21//!     wasm_byte_vec_t wat;
22//!     wasmer_byte_vec_new_from_string(&wat, "(foobar)");
23//!     wasm_byte_vec_t wasm;
24//!     wat2wasm(&wat, &wasm);
25//!
26//!     int error_length = wasmer_last_error_length();
27//!
28//!     // There is an error!
29//!     assert(error_length > 0);
30//!
31//!     char *error_message = malloc(error_length);
32//!     wasmer_last_error_message(error_message, error_length);
33//!     printf("Error message: %s\n", error_message);
34//!
35//!     // Side note: The error has now been cleared on the Rust side!
36//!     assert(wasmer_last_error_length() == 0);
37//!
38//!     // Free everything.
39//!     free(error_message);
40//!     wasm_byte_vec_delete(&wasm);
41//!     wasm_byte_vec_delete(&wat);
42//!
43//!     return 0;
44//! }
45//! #    })
46//! #    .success();
47//! # }
48//! ```
49
50use libc::{c_char, c_int};
51use std::cell::RefCell;
52use std::fmt::Display;
53use std::ptr::{self, NonNull};
54use std::slice;
55
56thread_local! {
57    static LAST_ERROR: RefCell<Option<String>> = const { RefCell::new(None) };
58}
59
60/// Rust function to register a new error.
61///
62/// # Example
63///
64/// ```rust,no_run
65/// # use wasmer::error::update_last_error;
66///
67/// update_last_error("Hello, World!");
68/// ```
69pub fn update_last_error<E: Display>(err: E) {
70    LAST_ERROR.with(|prev| {
71        *prev.borrow_mut() = Some(err.to_string());
72    });
73}
74
75/// Retrieve the most recent error, clearing it in the process.
76pub(crate) fn take_last_error() -> Option<String> {
77    LAST_ERROR.with(|prev| prev.borrow_mut().take())
78}
79
80/// Gets the length in bytes of the last error if any, zero otherwise. This
81/// includes th NUL terminator byte.
82///
83/// This can be used to dynamically allocate a buffer with the correct number of
84/// bytes needed to store a message.
85///
86/// # Example
87///
88/// See this module's documentation to get a complete example.
89// TODO(Amanieu): This should use size_t
90#[unsafe(no_mangle)]
91pub extern "C" fn wasmer_last_error_length() -> c_int {
92    LAST_ERROR.with(|prev| match *prev.borrow() {
93        Some(ref err) => err.len() as c_int + 1,
94        None => 0,
95    })
96}
97
98/// Gets the last error message if any into the provided buffer
99/// `buffer` up to the given `length`.
100///
101/// The `length` parameter must be large enough to store the last
102/// error message. Ideally, the value should come from
103/// [`wasmer_last_error_length`].
104///
105/// The function returns the length of the string in bytes, `-1` if an
106/// error occurs. Potential errors are:
107///
108///  * The `buffer` is a null pointer,
109///  * The `buffer` is too small to hold the error message.
110///
111/// Note: The error message always has a trailing NUL character.
112///
113/// Important note: If the provided `buffer` is non-null, once this
114/// function has been called, regardless whether it fails or succeeds,
115/// the error is cleared.
116///
117/// # Example
118///
119/// See this module's documentation to get a complete example.
120// TODO(Amanieu): This should use size_t
121#[unsafe(no_mangle)]
122pub unsafe extern "C" fn wasmer_last_error_message(
123    buffer: Option<NonNull<c_char>>,
124    length: c_int,
125) -> c_int {
126    let buffer = if let Some(buffer_inner) = buffer {
127        buffer_inner
128    } else {
129        // buffer pointer is null
130        return -1;
131    };
132
133    let error_message = match take_last_error() {
134        Some(err) => err,
135        None => return 0,
136    };
137
138    let length = length as usize;
139
140    if error_message.len() >= length {
141        // buffer is too small to hold the error message
142        return -1;
143    }
144
145    let buffer = unsafe { slice::from_raw_parts_mut(buffer.cast::<u8>().as_ptr(), length) };
146
147    unsafe {
148        ptr::copy_nonoverlapping(
149            error_message.as_ptr(),
150            buffer.as_mut_ptr(),
151            error_message.len(),
152        );
153    }
154
155    // Add a trailing null so people using the string as a `char *` don't
156    // accidentally read into garbage.
157    buffer[error_message.len()] = 0;
158
159    error_message.len() as c_int + 1
160}