Skip to content

Commit 0b6b792

Browse files
committed
Custom callables now cache their name, but show it in all safeguard levels
1 parent 8c55947 commit 0b6b792

File tree

1 file changed

+58
-34
lines changed

1 file changed

+58
-34
lines changed

godot-core/src/builtin/callable.rs

Lines changed: 58 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -307,13 +307,14 @@ impl Callable {
307307
// Could theoretically use `dyn` but would need:
308308
// - double boxing
309309
// - a type-erased workaround for PartialEq supertrait (which has a `Self` type parameter and thus is not object-safe)
310-
let userdata = CallableUserdata { inner: callable };
310+
let cached_callable = CachedRustCallable::new(callable);
311+
let userdata = CallableUserdata::new(cached_callable);
311312

312313
let info = CallableCustomInfo {
313314
// We could technically associate an object_id with the custom callable. is_valid_func would then check that for validity.
314315
callable_userdata: Box::into_raw(Box::new(userdata)) as *mut std::ffi::c_void,
315316
call_func: Some(rust_callable_call_custom::<C>),
316-
free_func: Some(rust_callable_destroy::<C>),
317+
free_func: Some(rust_callable_destroy::<CachedRustCallable<C>>),
317318
hash_func: Some(rust_callable_hash::<C>),
318319
equal_func: Some(rust_callable_equal::<C>),
319320
to_string_func: Some(rust_callable_to_string_display::<C>),
@@ -342,7 +343,7 @@ impl Callable {
342343
};
343344

344345
let object_id = wrapper.linked_object_id();
345-
let userdata = CallableUserdata { inner: wrapper };
346+
let userdata = CallableUserdata::new(wrapper);
346347

347348
let info = CallableCustomInfo {
348349
object_id,
@@ -607,6 +608,10 @@ mod custom_callable {
607608
}
608609

609610
impl<T> CallableUserdata<T> {
611+
pub fn new(inner: T) -> Self {
612+
Self { inner }
613+
}
614+
610615
/// # Safety
611616
/// Returns an unbounded reference. `void_ptr` must be a valid pointer to a `CallableUserdata`.
612617
unsafe fn inner_from_raw<'a>(void_ptr: *mut std::ffi::c_void) -> &'a mut T {
@@ -615,6 +620,33 @@ mod custom_callable {
615620
}
616621
}
617622

623+
/// Special wrapper for [`RustCallable`] based callables.
624+
///
625+
/// Separate from [`CallableUserdata`] because closure-based callables don't need to pay for overhead.
626+
pub(crate) struct CachedRustCallable<R: RustCallable> {
627+
rust_callable: R,
628+
cached_name: std::sync::OnceLock<String>,
629+
}
630+
631+
impl<R: RustCallable> CachedRustCallable<R> {
632+
pub fn new(inner: R) -> Self {
633+
Self {
634+
rust_callable: inner,
635+
cached_name: std::sync::OnceLock::new(),
636+
}
637+
}
638+
639+
/// Gets or initializes the cached name lazily. The name is computed on first access by calling `to_string()` on the inner value.
640+
pub fn get_name_lazy(&mut self) -> &str
641+
where
642+
R: fmt::Display,
643+
{
644+
self.cached_name
645+
.get_or_init(|| self.rust_callable.to_string())
646+
.as_str()
647+
}
648+
}
649+
618650
pub(crate) struct FnWrapper<F> {
619651
pub(super) rust_function: F,
620652
pub(super) name: CowStr,
@@ -631,19 +663,6 @@ mod custom_callable {
631663
}
632664
}
633665

634-
/// Returns the name for safeguard-enabled builds, or `"<optimized out>"` otherwise.
635-
macro_rules! name_or_optimized {
636-
($($code:tt)*) => {
637-
{
638-
#[cfg(safeguards_balanced)]
639-
{ $($code)* }
640-
641-
#[cfg(not(safeguards_balanced))]
642-
{ "<optimized out>" }
643-
}
644-
};
645-
}
646-
647666
/// Represents a custom callable object defined in Rust.
648667
///
649668
/// This trait has a single method, `invoke`, which is called upon invocation.
@@ -680,16 +699,19 @@ mod custom_callable {
680699
) {
681700
let arg_refs: &[&Variant] = Variant::borrow_ref_slice(p_args, p_argument_count as usize);
682701

683-
let name = name_or_optimized! {
684-
let c: &C = CallableUserdata::inner_from_raw(callable_userdata);
685-
c.to_string()
702+
// Initialize cached name lazily on first access.
703+
let name = {
704+
let cached: &mut CachedRustCallable<C> =
705+
CallableUserdata::inner_from_raw(callable_userdata);
706+
cached.get_name_lazy()
686707
};
687-
let ctx = meta::CallContext::custom_callable(&name);
708+
let ctx = meta::CallContext::custom_callable(name);
688709

689710
crate::private::handle_fallible_varcall(&ctx, &mut *r_error, move || {
690711
// Get the RustCallable again inside closure so it doesn't have to be UnwindSafe.
691-
let c: &mut C = CallableUserdata::inner_from_raw(callable_userdata);
692-
let result = c.invoke(arg_refs);
712+
let cached: &mut CachedRustCallable<C> =
713+
CallableUserdata::inner_from_raw(callable_userdata);
714+
let result = cached.rust_callable.invoke(arg_refs);
693715
meta::varcall_return_checked(Ok(result), r_return, r_error);
694716
Ok(())
695717
});
@@ -729,37 +751,38 @@ mod custom_callable {
729751
});
730752
}
731753

754+
/** `T` here is entire object stored in [`CallableUserdata`], not just the actual [`RustCallable`] or closure instance. */
732755
pub unsafe extern "C" fn rust_callable_destroy<T>(callable_userdata: *mut std::ffi::c_void) {
733756
let rust_ptr = callable_userdata as *mut CallableUserdata<T>;
734757
let _drop = Box::from_raw(rust_ptr);
735758
}
736759

737-
pub unsafe extern "C" fn rust_callable_hash<T: Hash>(
760+
pub unsafe extern "C" fn rust_callable_hash<C: RustCallable + Hash>(
738761
callable_userdata: *mut std::ffi::c_void,
739762
) -> u32 {
740-
let c: &T = CallableUserdata::<T>::inner_from_raw(callable_userdata);
763+
let cached: &CachedRustCallable<C> = CallableUserdata::inner_from_raw(callable_userdata);
741764

742765
// Just cut off top bits, not best-possible hash.
743-
sys::hash_value(c) as u32
766+
sys::hash_value(&cached.rust_callable) as u32
744767
}
745768

746-
pub unsafe extern "C" fn rust_callable_equal<T: PartialEq>(
769+
pub unsafe extern "C" fn rust_callable_equal<C: RustCallable + PartialEq>(
747770
callable_userdata_a: *mut std::ffi::c_void,
748771
callable_userdata_b: *mut std::ffi::c_void,
749772
) -> sys::GDExtensionBool {
750-
let a: &T = CallableUserdata::inner_from_raw(callable_userdata_a);
751-
let b: &T = CallableUserdata::inner_from_raw(callable_userdata_b);
773+
let a: &CachedRustCallable<C> = CallableUserdata::inner_from_raw(callable_userdata_a);
774+
let b: &CachedRustCallable<C> = CallableUserdata::inner_from_raw(callable_userdata_b);
752775

753-
sys::conv::bool_to_sys(a == b)
776+
sys::conv::bool_to_sys(a.rust_callable == b.rust_callable)
754777
}
755778

756-
pub unsafe extern "C" fn rust_callable_to_string_display<T: fmt::Display>(
779+
pub unsafe extern "C" fn rust_callable_to_string_display<C: RustCallable + fmt::Display>(
757780
callable_userdata: *mut std::ffi::c_void,
758781
r_is_valid: *mut sys::GDExtensionBool,
759782
r_out: sys::GDExtensionStringPtr,
760783
) {
761-
let c: &T = CallableUserdata::inner_from_raw(callable_userdata);
762-
let s = GString::from(&c.to_string());
784+
let cached: &CachedRustCallable<C> = CallableUserdata::inner_from_raw(callable_userdata);
785+
let s = GString::from(&cached.rust_callable.to_string());
763786

764787
s.move_into_string_ptr(r_out);
765788
*r_is_valid = sys::conv::SYS_TRUE;
@@ -781,8 +804,9 @@ mod custom_callable {
781804
pub unsafe extern "C" fn rust_callable_is_valid_custom<C: RustCallable>(
782805
callable_userdata: *mut std::ffi::c_void,
783806
) -> sys::GDExtensionBool {
784-
let w: &mut C = CallableUserdata::inner_from_raw(callable_userdata);
785-
let valid = w.is_valid();
807+
let cached: &mut CachedRustCallable<C> =
808+
CallableUserdata::inner_from_raw(callable_userdata);
809+
let valid = cached.rust_callable.is_valid();
786810

787811
sys::conv::bool_to_sys(valid)
788812
}

0 commit comments

Comments
 (0)