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 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    use inline_c::assert_c;
149
150    #[allow(
151        unexpected_cfgs,
152        reason = "tools like cargo-llvm-coverage pass --cfg coverage"
153    )]
154    #[cfg_attr(coverage_nightly, coverage(off))]
155    #[test]
156    fn test_trap_message_null_terminated() {
157        (assert_c! {
158            #include "tests/wasmer.h"
159
160            int main() {
161                wasm_engine_t* engine = wasm_engine_new();
162                wasm_store_t* store = wasm_store_new(engine);
163
164                wasm_message_t original_message;
165                wasm_name_new_from_string_nt(&original_message, "foobar");
166                assert(original_message.size == 7); // 6 for `foobar` + 1 for nul byte.
167
168                wasm_trap_t* trap = wasm_trap_new(store, &original_message);
169                assert(trap);
170
171                wasm_message_t retrieved_message;
172                wasm_trap_message(trap, &retrieved_message);
173                assert(retrieved_message.size == 7);
174
175                wasm_name_delete(&original_message);
176                wasm_name_delete(&retrieved_message);
177                wasm_trap_delete(trap);
178                wasm_store_delete(store);
179                wasm_engine_delete(engine);
180
181                return 0;
182            }
183        })
184        .success();
185    }
186
187    #[allow(
188        unexpected_cfgs,
189        reason = "tools like cargo-llvm-coverage pass --cfg coverage"
190    )]
191    #[cfg_attr(coverage_nightly, coverage(off))]
192    #[test]
193    fn test_trap_message_not_null_terminated() {
194        (assert_c! {
195            #include "tests/wasmer.h"
196
197            int main() {
198                wasm_engine_t* engine = wasm_engine_new();
199                wasm_store_t* store = wasm_store_new(engine);
200
201                wasm_message_t original_message;
202                wasm_name_new_from_string(&original_message, "foobar");
203                assert(original_message.size == 6); // 6 for `foobar` + 0 for nul byte.
204
205                wasm_trap_t* trap = wasm_trap_new(store, &original_message);
206                assert(trap);
207
208                wasm_message_t retrieved_message;
209                wasm_trap_message(trap, &retrieved_message);
210                assert(retrieved_message.size == 7);
211
212                wasm_name_delete(&original_message);
213                wasm_name_delete(&retrieved_message);
214                wasm_trap_delete(trap);
215                wasm_store_delete(store);
216                wasm_engine_delete(engine);
217
218                return 0;
219            }
220        })
221        .success();
222    }
223}