Skip to content

Commit 63b7da5

Browse files
committed
Dedicated type for panic payloads
1 parent f44b58b commit 63b7da5

File tree

2 files changed

+29
-9
lines changed

2 files changed

+29
-9
lines changed

godot-core/src/meta/error/call_error.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use crate::builtin::{Variant, VariantType};
99
use crate::meta::error::{ConvertError, ErasedConvertError};
1010
use crate::meta::{CallContext, ToGodot};
11+
use crate::private::PanicPayload;
1112
use crate::sys;
1213
use godot_ffi::join_debug;
1314
use std::error::Error;
@@ -300,11 +301,13 @@ impl CallError {
300301
}
301302

302303
#[doc(hidden)]
303-
pub fn failed_by_user_panic(call_ctx: &CallContext, reason: String) -> Self {
304+
pub fn failed_by_user_panic(call_ctx: &CallContext, panic_payload: PanicPayload) -> Self {
304305
// This can cause the panic message to be printed twice in some scenarios (e.g. bind_mut() borrow failure).
305306
// But in other cases (e.g. itest `dynamic_call_with_panic`), it is only printed once.
306307
// Would need some work to have a consistent experience.
307308

309+
let reason = panic_payload.into_panic_message();
310+
308311
Self::new(call_ctx, format!("function panicked: {reason}"), None)
309312
}
310313

godot-core/src/private.rs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ pub fn is_class_runtime(is_tool: bool) -> bool {
218218
}
219219

220220
// ----------------------------------------------------------------------------------------------------------------------------------------------
221-
// Panic handling
221+
// Panic *hook* management
222222

223223
pub fn extract_panic_message(err: &(dyn Send + std::any::Any)) -> String {
224224
if let Some(s) = err.downcast_ref::<&'static str>() {
@@ -370,13 +370,31 @@ pub fn get_gdext_panic_context() -> Option<String> {
370370
None
371371
}
372372

373+
// ----------------------------------------------------------------------------------------------------------------------------------------------
374+
// Panic unwinding and catching
375+
376+
pub struct PanicPayload {
377+
payload: Box<dyn std::any::Any + Send + 'static>,
378+
}
379+
380+
impl PanicPayload {
381+
pub fn new(payload: Box<dyn std::any::Any + Send + 'static>) -> Self {
382+
Self { payload }
383+
}
384+
385+
// While this could be `&self`, it's usually good practice to pass panic payloads around linearly and have only 1 representation at a time.
386+
pub fn into_panic_message(self) -> String {
387+
extract_panic_message(self.payload.as_ref())
388+
}
389+
}
390+
373391
/// Executes `code`. If a panic is thrown, it is caught and an error message is printed to Godot.
374392
///
375393
/// Returns `Err(message)` if a panic occurred, and `Ok(result)` with the result of `code` otherwise.
376394
///
377395
/// In contrast to [`handle_varcall_panic`] and [`handle_ptrcall_panic`], this function is not intended for use in `try_` functions,
378396
/// where the error is propagated as a `CallError` in a global variable.
379-
pub fn handle_panic<E, F, R>(error_context: E, code: F) -> Result<R, String>
397+
pub fn handle_panic<E, F, R>(error_context: E, code: F) -> Result<R, PanicPayload>
380398
where
381399
E: Fn() -> String,
382400
F: FnOnce() -> R + std::panic::UnwindSafe,
@@ -390,8 +408,7 @@ where
390408
cell.borrow_mut().push_function(&error_context)
391409
});
392410

393-
let result =
394-
std::panic::catch_unwind(code).map_err(|payload| extract_panic_message(payload.as_ref()));
411+
let result = std::panic::catch_unwind(code).map_err(PanicPayload::new);
395412

396413
#[cfg(debug_assertions)]
397414
ERROR_CONTEXT_STACK.with(|cell| cell.borrow_mut().pop_function());
@@ -407,8 +424,8 @@ pub fn handle_varcall_panic<F, R>(
407424
) where
408425
F: FnOnce() -> Result<R, CallError> + std::panic::UnwindSafe,
409426
{
410-
let outcome: Result<Result<R, CallError>, String> =
411-
handle_panic(|| format!("{call_ctx}"), code);
427+
let outcome: Result<Result<R, CallError>, PanicPayload> =
428+
handle_panic(|| call_ctx.to_string(), code);
412429

413430
let call_error = match outcome {
414431
// All good.
@@ -437,14 +454,14 @@ pub fn handle_ptrcall_panic<F, R>(call_ctx: &CallContext, code: F)
437454
where
438455
F: FnOnce() -> R + std::panic::UnwindSafe,
439456
{
440-
let outcome: Result<R, String> = handle_panic(|| format!("{call_ctx}"), code);
457+
let outcome: Result<R, PanicPayload> = handle_panic(|| call_ctx.to_string(), code);
441458

442459
let call_error = match outcome {
443460
// All good.
444461
Ok(_result) => return,
445462

446463
// Panic occurred (typically through user): forward message.
447-
Err(panic_msg) => CallError::failed_by_user_panic(call_ctx, panic_msg),
464+
Err(payload) => CallError::failed_by_user_panic(call_ctx, payload),
448465
};
449466

450467
let _id = report_call_error(call_error, false);

0 commit comments

Comments
 (0)