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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
//! 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 inline_c::assert_c;
//! # fn main() {
//! #    (assert_c! {
//! # #include "tests/wasmer_wasm.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::error::Error;
use std::fmt::{self, Display, Formatter};
use std::ptr::{self, NonNull};
use std::slice;

thread_local! {
    static LAST_ERROR: RefCell<Option<Box<dyn Error>>> = RefCell::new(None);
}

/// Rust function to register a new error.
///
/// # Example
///
/// ```rust,no_run
/// # use wasmer_c_api::error::{update_last_error, CApiError};
///
/// update_last_error(CApiError {
///     msg: "Hello, World!".to_string(),
/// });
/// ```
pub fn update_last_error<E: Error + 'static>(err: E) {
    LAST_ERROR.with(|prev| {
        *prev.borrow_mut() = Some(Box::new(err));
    });
}

/// Retrieve the most recent error, clearing it in the process.
pub(crate) fn take_last_error() -> Option<Box<dyn Error>> {
    LAST_ERROR.with(|prev| prev.borrow_mut().take())
}

/// Gets the length in bytes of the last error if any, zero otherwise.
///
/// 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.
#[no_mangle]
pub extern "C" fn wasmer_last_error_length() -> c_int {
    LAST_ERROR.with(|prev| match *prev.borrow() {
        Some(ref err) => err.to_string().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.
#[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.to_string(),
        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
}

/// Rust type to represent a C API error.
#[derive(Debug)]
pub struct CApiError {
    /// The error message.
    pub msg: String,
}

impl Display for CApiError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{}", &self.msg)
    }
}

impl Error for CApiError {}