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}