Skip to main content

luaur_vm/functions/
install_lua_exception_panic_hook.rs

1//! Silence the default panic-hook output for the VM's `longjmp`-emulation unwinds.
2//!
3//! Luau's `luaD_throw` uses C++ exceptions for control flow; the faithful Rust
4//! port (`lua_d_throw`) emulates this with [`std::panic::panic_any`] carrying a
5//! [`lua_exception`], caught at the [`luaD_rawrunprotected`] boundary
6//! (`VM/src/ldo.cpp`). These are NOT crashes — they are the normal mechanism by
7//! which an ordinary Lua runtime error (`error(..)`, a failed `assert`, a type
8//! error) propagates up to `pcall`/the resume boundary.
9//!
10//! The default Rust panic hook, however, prints `thread '...' panicked at ...`
11//! to stderr for *every* unwind, including these caught ones — so a perfectly
12//! normal Lua error made the CLI look like it had crashed (a `Box<dyn Any>`
13//! message leaking before `catch_unwind` swallowed the payload).
14//!
15//! [`install_lua_exception_panic_hook`] installs (exactly once, process-wide) a
16//! hook that suppresses the message for `lua_exception` payloads while
17//! delegating every other panic to the previously-installed hook unchanged, so
18//! genuine Rust bugs still surface with their full diagnostics.
19
20use crate::records::lua_exception::lua_exception;
21use std::sync::Once;
22
23static INSTALL: Once = Once::new();
24
25/// Install the `lua_exception`-silencing panic hook (idempotent; the first call
26/// wins, subsequent calls are no-ops). Safe to call from any VM entry point.
27pub fn install_lua_exception_panic_hook() {
28    INSTALL.call_once(|| {
29        let previous = std::panic::take_hook();
30        std::panic::set_hook(Box::new(move |info| {
31            // A `lua_exception` payload is the VM's longjmp emulation, caught at
32            // `luaD_rawrunprotected`; do not print anything for it.
33            if info.payload().is::<lua_exception>() {
34                return;
35            }
36            // Everything else is a real panic — preserve the prior behavior.
37            previous(info);
38        }));
39    });
40}