Skip to main content

wasmtime_c_api/
trap.rs

1use crate::{wasm_frame_vec_t, wasm_instance_t, wasm_name_t, wasm_store_t};
2use std::sync::OnceLock;
3use wasmtime::{Error, Trap, WasmBacktrace, format_err};
4
5// Help ensure the Rust enum matches the C one.  If any of these assertions
6// fail, please update both this code and `trap.h` to sync them with
7// `trap_encoding.rs`.
8const _: () = {
9    assert!(Trap::StackOverflow as u8 == 0);
10    assert!(Trap::MemoryOutOfBounds as u8 == 1);
11    assert!(Trap::HeapMisaligned as u8 == 2);
12    assert!(Trap::TableOutOfBounds as u8 == 3);
13    assert!(Trap::IndirectCallToNull as u8 == 4);
14    assert!(Trap::BadSignature as u8 == 5);
15    assert!(Trap::IntegerOverflow as u8 == 6);
16    assert!(Trap::IntegerDivisionByZero as u8 == 7);
17    assert!(Trap::BadConversionToInteger as u8 == 8);
18    assert!(Trap::UnreachableCodeReached as u8 == 9);
19    assert!(Trap::Interrupt as u8 == 10);
20    assert!(Trap::OutOfFuel as u8 == 11);
21    assert!(Trap::AtomicWaitNonSharedMemory as u8 == 12);
22    assert!(Trap::NullReference as u8 == 13);
23    assert!(Trap::ArrayOutOfBounds as u8 == 14);
24    assert!(Trap::AllocationTooLarge as u8 == 15);
25    assert!(Trap::CastFailure as u8 == 16);
26    assert!(Trap::CannotEnterComponent as u8 == 17);
27    assert!(Trap::NoAsyncResult as u8 == 18);
28    assert!(Trap::UnhandledTag as u8 == 19);
29    assert!(Trap::ContinuationAlreadyConsumed as u8 == 20);
30    assert!(Trap::DisabledOpcode as u8 == 21);
31    assert!(Trap::AsyncDeadlock as u8 == 22);
32    assert!(Trap::CannotLeaveComponent as u8 == 23);
33    assert!(Trap::CannotBlockSyncTask as u8 == 24);
34    assert!(Trap::InvalidChar as u8 == 25);
35    assert!(Trap::DebugAssertStringEncodingFinished as u8 == 26);
36    assert!(Trap::DebugAssertEqualCodeUnits as u8 == 27);
37    assert!(Trap::DebugAssertPointerAligned as u8 == 28);
38    assert!(Trap::DebugAssertUpperBitsUnset as u8 == 29);
39    assert!(Trap::StringOutOfBounds as u8 == 30);
40    assert!(Trap::ListOutOfBounds as u8 == 31);
41    assert!(Trap::InvalidDiscriminant as u8 == 32);
42    assert!(Trap::UnalignedPointer as u8 == 33);
43    assert!(Trap::TaskCancelNotCancelled as u8 == 34);
44    assert!(Trap::TaskCancelOrReturnTwice as u8 == 35);
45    assert!(Trap::SubtaskCancelAfterTerminal as u8 == 36);
46    assert!(Trap::TaskReturnInvalid as u8 == 37);
47    assert!(Trap::WaitableSetDropHasWaiters as u8 == 38);
48    assert!(Trap::SubtaskDropNotResolved as u8 == 39);
49    assert!(Trap::ThreadNewIndirectInvalidType as u8 == 40);
50    assert!(Trap::ThreadNewIndirectUninitialized as u8 == 41);
51    assert!(Trap::BackpressureOverflow as u8 == 42);
52    assert!(Trap::UnsupportedCallbackCode as u8 == 43);
53    assert!(Trap::CannotResumeThread as u8 == 44);
54    assert!(Trap::ConcurrentFutureStreamOp as u8 == 45);
55    assert!(Trap::ReferenceCountOverflow as u8 == 46);
56    assert!(Trap::StreamOpTooBig as u8 == 47);
57    assert!(Trap::WaitableSyncAndAsync as u8 == 48);
58    assert!(Trap::UncaughtException as u8 == 49);
59    assert!(Trap::ReadFromDroppedStream as u8 == 50);
60    assert!(Trap::WriteToDroppedStream as u8 == 51);
61    assert!(Trap::WriteToDroppedFuture as u8 == 52);
62    assert!(Trap::LiftDroppedStream as u8 == 53);
63    assert!(Trap::TaskReturnOrCancelSyncLifted as u8 == 54);
64};
65
66#[repr(C)]
67pub struct wasm_trap_t {
68    pub(crate) error: Error,
69}
70
71// This is currently only needed for the `wasm_trap_copy` API in the C API.
72//
73// For now the impl here is "fake it til you make it" since this is losing
74// context by only cloning the error string.
75impl Clone for wasm_trap_t {
76    fn clone(&self) -> wasm_trap_t {
77        wasm_trap_t {
78            error: format_err!("{:?}", self.error),
79        }
80    }
81}
82
83wasmtime_c_api_macros::declare_ref!(wasm_trap_t);
84
85impl wasm_trap_t {
86    pub(crate) fn new(error: Error) -> wasm_trap_t {
87        wasm_trap_t { error }
88    }
89}
90
91#[repr(C)]
92#[derive(Clone)]
93pub struct wasm_frame_t<'a> {
94    trace: &'a WasmBacktrace,
95    idx: usize,
96    func_name: OnceLock<Option<wasm_name_t>>,
97    module_name: OnceLock<Option<wasm_name_t>>,
98}
99
100wasmtime_c_api_macros::declare_own!(wasm_frame_t);
101
102pub type wasm_message_t = wasm_name_t;
103
104#[unsafe(no_mangle)]
105pub extern "C" fn wasm_trap_new(
106    _store: &wasm_store_t,
107    message: &wasm_message_t,
108) -> Box<wasm_trap_t> {
109    let message = message.as_slice();
110    if message[message.len() - 1] != 0 {
111        panic!("wasm_trap_new message stringz expected");
112    }
113    let message = String::from_utf8_lossy(&message[..message.len() - 1]);
114    Box::new(wasm_trap_t {
115        error: Error::msg(message.into_owned()),
116    })
117}
118
119#[unsafe(no_mangle)]
120pub unsafe extern "C" fn wasmtime_trap_new(message: *const u8, len: usize) -> Box<wasm_trap_t> {
121    let bytes = crate::slice_from_raw_parts(message, len);
122    let message = String::from_utf8_lossy(&bytes);
123    Box::new(wasm_trap_t {
124        error: Error::msg(message.into_owned()),
125    })
126}
127
128#[unsafe(no_mangle)]
129pub unsafe extern "C" fn wasmtime_trap_new_code(code: u8) -> Box<wasm_trap_t> {
130    let trap = Trap::from_u8(code).unwrap();
131    Box::new(wasm_trap_t {
132        error: Error::new(trap),
133    })
134}
135
136#[unsafe(no_mangle)]
137pub extern "C" fn wasm_trap_message(trap: &wasm_trap_t, out: &mut wasm_message_t) {
138    let mut buffer = Vec::new();
139    buffer.extend_from_slice(format!("{:?}", trap.error).as_bytes());
140    buffer.reserve_exact(1);
141    buffer.push(0);
142    out.set_buffer(buffer);
143}
144
145#[unsafe(no_mangle)]
146pub extern "C" fn wasm_trap_origin(raw: &wasm_trap_t) -> Option<Box<wasm_frame_t<'_>>> {
147    let trace = match raw.error.downcast_ref::<WasmBacktrace>() {
148        Some(trap) => trap,
149        None => return None,
150    };
151    if trace.frames().len() > 0 {
152        Some(Box::new(wasm_frame_t {
153            trace,
154            idx: 0,
155            func_name: OnceLock::new(),
156            module_name: OnceLock::new(),
157        }))
158    } else {
159        None
160    }
161}
162
163#[unsafe(no_mangle)]
164pub extern "C" fn wasm_trap_trace<'a>(raw: &'a wasm_trap_t, out: &mut wasm_frame_vec_t<'a>) {
165    error_trace(&raw.error, out)
166}
167
168pub(crate) fn error_trace<'a>(error: &'a Error, out: &mut wasm_frame_vec_t<'a>) {
169    let trace = match error.downcast_ref::<WasmBacktrace>() {
170        Some(trap) => trap,
171        None => return out.set_buffer(Vec::new()),
172    };
173    let vec = (0..trace.frames().len())
174        .map(|idx| {
175            Some(Box::new(wasm_frame_t {
176                trace,
177                idx,
178                func_name: OnceLock::new(),
179                module_name: OnceLock::new(),
180            }))
181        })
182        .collect();
183    out.set_buffer(vec);
184}
185
186#[unsafe(no_mangle)]
187pub extern "C" fn wasmtime_trap_code(raw: &wasm_trap_t, code: &mut u8) -> bool {
188    let trap = match raw.error.downcast_ref::<Trap>() {
189        Some(trap) => trap,
190        None => return false,
191    };
192    *code = *trap as u8;
193    true
194}
195
196#[unsafe(no_mangle)]
197pub extern "C" fn wasm_frame_func_index(frame: &wasm_frame_t<'_>) -> u32 {
198    frame.trace.frames()[frame.idx].func_index()
199}
200
201#[unsafe(no_mangle)]
202pub extern "C" fn wasmtime_frame_func_name<'a>(
203    frame: &'a wasm_frame_t<'_>,
204) -> Option<&'a wasm_name_t> {
205    frame
206        .func_name
207        .get_or_init(|| {
208            frame.trace.frames()[frame.idx]
209                .func_name()
210                .map(|s| wasm_name_t::from(s.to_string().into_bytes()))
211        })
212        .as_ref()
213}
214
215#[unsafe(no_mangle)]
216pub extern "C" fn wasmtime_frame_module_name<'a>(
217    frame: &'a wasm_frame_t<'_>,
218) -> Option<&'a wasm_name_t> {
219    frame
220        .module_name
221        .get_or_init(|| {
222            frame.trace.frames()[frame.idx]
223                .module()
224                .name()
225                .map(|s| wasm_name_t::from(s.to_string().into_bytes()))
226        })
227        .as_ref()
228}
229
230#[unsafe(no_mangle)]
231pub extern "C" fn wasm_frame_func_offset(frame: &wasm_frame_t<'_>) -> usize {
232    frame.trace.frames()[frame.idx]
233        .func_offset()
234        .unwrap_or(usize::MAX)
235}
236
237#[unsafe(no_mangle)]
238pub extern "C" fn wasm_frame_instance(_arg1: *const wasm_frame_t<'_>) -> *mut wasm_instance_t {
239    unimplemented!("wasm_frame_instance")
240}
241
242#[unsafe(no_mangle)]
243pub extern "C" fn wasm_frame_module_offset(frame: &wasm_frame_t<'_>) -> usize {
244    frame.trace.frames()[frame.idx]
245        .module_offset()
246        .unwrap_or(usize::MAX)
247}
248
249#[unsafe(no_mangle)]
250pub extern "C" fn wasm_frame_copy<'a>(frame: &wasm_frame_t<'a>) -> Box<wasm_frame_t<'a>> {
251    Box::new(frame.clone())
252}