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>> = 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
}