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