wasmer_c_api/wasm_c_api/
trap.rs

1use super::store::wasm_store_t;
2use super::types::{wasm_byte_vec_t, wasm_message_t};
3use super::types::{wasm_frame_t, wasm_frame_vec_t};
4use std::ffi::CString;
5use wasmer_api::RuntimeError;
6
7// opaque type which is a `RuntimeError`
8#[allow(non_camel_case_types)]
9pub struct wasm_trap_t {
10    pub(crate) inner: RuntimeError,
11}
12
13impl From<RuntimeError> for wasm_trap_t {
14    fn from(other: RuntimeError) -> Self {
15        Self { inner: other }
16    }
17}
18
19/// Create a new trap message.
20///
21/// Be careful, the message is typed with `wasm_message_t` which
22/// represents a null-terminated string.
23///
24/// # Example
25///
26/// See the module's documentation for a complete example.
27#[unsafe(no_mangle)]
28pub unsafe extern "C" fn wasm_trap_new(
29    _store: &mut wasm_store_t,
30    message: &wasm_message_t,
31) -> Option<Box<wasm_trap_t>> {
32    let message_bytes = message.as_slice();
33
34    // The trap message is typed with `wasm_message_t` which is a
35    // typeref to `wasm_name_t` with the exception that it's a
36    // null-terminated string. `RuntimeError` must contain a valid
37    // Rust `String` that doesn't contain a null byte. We must ensure
38    // this behavior.
39    let runtime_error = match CString::new(message_bytes) {
40        // The string is well-formed and doesn't contain a nul byte.
41        Ok(cstring) => RuntimeError::new(cstring.into_string().ok()?),
42
43        // The string is well-formed but is nul-terminated. Let's
44        // create a `String` which is null-terminated too.
45        Err(nul_error) if nul_error.nul_position() + 1 == message_bytes.len() => {
46            let mut vec = nul_error.into_vec();
47            vec.pop();
48
49            RuntimeError::new(String::from_utf8(vec).ok()?)
50        }
51
52        // The string not well-formed.
53        Err(_) => return None,
54    };
55
56    let trap = runtime_error.into();
57
58    Some(Box::new(trap))
59}
60
61/// Deletes a trap.
62///
63/// # Example
64///
65/// See the module's documentation for a complete example.
66#[unsafe(no_mangle)]
67pub unsafe extern "C" fn wasm_trap_delete(_trap: Option<Box<wasm_trap_t>>) {}
68
69/// Gets the message attached to the trap.
70///
71/// # Example
72///
73/// ```rust
74/// # use wasmer_inline_c::assert_c;
75/// # fn main() {
76/// #    (assert_c! {
77/// # #include "tests/wasmer.h"
78/// #
79/// int main() {
80///     // Create an engine and a store.
81///     wasm_engine_t* engine = wasm_engine_new();
82///     wasm_store_t* store = wasm_store_new(engine);
83///
84///     // Create the trap message.
85///     wasm_message_t message;
86///     wasm_name_new_from_string_nt(&message, "foobar");
87///
88///     // Create the trap with its message.
89///     // The backtrace will be generated automatically.
90///     wasm_trap_t* trap = wasm_trap_new(store, &message);
91///     assert(trap);
92///
93///     // Get the trap's message back.
94///     wasm_message_t retrieved_message;
95///     wasm_trap_message(trap, &retrieved_message);
96///     assert(retrieved_message.size == message.size);
97///
98///     // Free everything.
99///     wasm_name_delete(&message);
100///     wasm_name_delete(&retrieved_message);
101///     wasm_trap_delete(trap);
102///     wasm_store_delete(store);
103///     wasm_engine_delete(engine);
104///
105///     return 0;
106/// }
107/// #    })
108/// #    .success();
109/// # }
110/// ```
111#[unsafe(no_mangle)]
112pub unsafe extern "C" fn wasm_trap_message(
113    trap: &wasm_trap_t,
114    // own
115    out: &mut wasm_byte_vec_t,
116) {
117    let message = trap.inner.message();
118    let mut byte_vec = message.into_bytes();
119    byte_vec.push(0);
120
121    out.set_buffer(byte_vec);
122}
123
124/// Gets the origin frame attached to the trap.
125#[unsafe(no_mangle)]
126pub unsafe extern "C" fn wasm_trap_origin(trap: &wasm_trap_t) -> Option<Box<wasm_frame_t>> {
127    trap.inner.trace().first().map(Into::into).map(Box::new)
128}
129
130/// Gets the trace (as a list of frames) attached to the trap.
131#[unsafe(no_mangle)]
132pub unsafe extern "C" fn wasm_trap_trace(
133    trap: &wasm_trap_t,
134    // own
135    out: &mut wasm_frame_vec_t,
136) {
137    let frames = trap.inner.trace();
138    out.set_buffer(
139        frames
140            .iter()
141            .map(|frame| Some(Box::new(frame.into())))
142            .collect(),
143    );
144}
145
146#[cfg(test)]
147mod tests {
148    #[cfg(not(target_os = "windows"))]
149    use inline_c::assert_c;
150    #[cfg(target_os = "windows")]
151    use wasmer_inline_c::assert_c;
152
153    #[allow(
154        unexpected_cfgs,
155        reason = "tools like cargo-llvm-coverage pass --cfg coverage"
156    )]
157    #[cfg_attr(coverage, ignore)]
158    #[test]
159    fn test_trap_message_null_terminated() {
160        (assert_c! {
161            #include "tests/wasmer.h"
162
163            int main() {
164                wasm_engine_t* engine = wasm_engine_new();
165                wasm_store_t* store = wasm_store_new(engine);
166
167                wasm_message_t original_message;
168                wasm_name_new_from_string_nt(&original_message, "foobar");
169                assert(original_message.size == 7); // 6 for `foobar` + 1 for nul byte.
170
171                wasm_trap_t* trap = wasm_trap_new(store, &original_message);
172                assert(trap);
173
174                wasm_message_t retrieved_message;
175                wasm_trap_message(trap, &retrieved_message);
176                assert(retrieved_message.size == 7);
177
178                wasm_name_delete(&original_message);
179                wasm_name_delete(&retrieved_message);
180                wasm_trap_delete(trap);
181                wasm_store_delete(store);
182                wasm_engine_delete(engine);
183
184                return 0;
185            }
186        })
187        .success();
188    }
189
190    #[allow(
191        unexpected_cfgs,
192        reason = "tools like cargo-llvm-coverage pass --cfg coverage"
193    )]
194    #[cfg_attr(coverage, ignore)]
195    #[test]
196    fn test_trap_message_not_null_terminated() {
197        (assert_c! {
198            #include "tests/wasmer.h"
199
200            int main() {
201                wasm_engine_t* engine = wasm_engine_new();
202                wasm_store_t* store = wasm_store_new(engine);
203
204                wasm_message_t original_message;
205                wasm_name_new_from_string(&original_message, "foobar");
206                assert(original_message.size == 6); // 6 for `foobar` + 0 for nul byte.
207
208                wasm_trap_t* trap = wasm_trap_new(store, &original_message);
209                assert(trap);
210
211                wasm_message_t retrieved_message;
212                wasm_trap_message(trap, &retrieved_message);
213                assert(retrieved_message.size == 7);
214
215                wasm_name_delete(&original_message);
216                wasm_name_delete(&retrieved_message);
217                wasm_trap_delete(trap);
218                wasm_store_delete(store);
219                wasm_engine_delete(engine);
220
221                return 0;
222            }
223        })
224        .success();
225    }
226}