@@ -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