Skip to main content

worker/
panic_abort.rs

1use std::panic;
2
3#[cfg(target_arch = "wasm32")]
4use wasm_bindgen::prelude::*;
5
6#[cfg(target_arch = "wasm32")]
7use std::cell::Cell;
8
9#[cfg(target_arch = "wasm32")]
10thread_local! {
11    static PANIC_CALLBACK: Cell<Option<js_sys::Function>> = Cell::new(None);
12}
13
14#[cfg(target_arch = "wasm32")]
15#[wasm_bindgen(js_name = "setPanicHook")]
16pub fn set_panic_hook(callback: js_sys::Function) {
17    PANIC_CALLBACK.with(|f| f.set(Some(callback)));
18    set_once();
19}
20
21#[cfg(not(target_arch = "wasm32"))]
22pub fn set_panic_hook(_callback: ()) {
23    // No-op on non-wasm targets
24}
25
26#[allow(deprecated)]
27#[cfg(target_arch = "wasm32")]
28fn hook_impl(info: &panic::PanicInfo) {
29    let message = info.to_string();
30
31    PANIC_CALLBACK.with(|f| {
32        if let Some(callback) = f.take() {
33            use js_sys::JsString;
34
35            if let Err(e) = callback.call1(&JsValue::UNDEFINED, &JsString::from(message)) {
36                web_sys::console::error_2(&"Failed to call panic callback:".into(), &e);
37            }
38        }
39    });
40}
41
42#[allow(deprecated)]
43#[cfg(not(target_arch = "wasm32"))]
44fn hook_impl(_info: &panic::PanicInfo) {
45    // On non-wasm targets, we don't have contexts to abort
46    // This is a no-op, but maintains the same interface
47}
48
49/// Set the WASM reinitialization panic hook the first time this is called.
50/// Subsequent invocations do nothing.
51#[allow(dead_code)]
52#[inline]
53fn set_once() {
54    use std::sync::Once;
55    static SET_HOOK: Once = Once::new();
56    SET_HOOK.call_once(|| {
57        let default_hook = panic::take_hook();
58        panic::set_hook(Box::new(move |panic_info| {
59            // First call the existing hook (console_error_panic_hook if set)
60            default_hook(panic_info);
61            hook_impl(panic_info);
62        }));
63    });
64}