godot_core/registry/
plugin.rs

1/*
2 * Copyright (c) godot-rust; Bromeon and contributors.
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
6 */
7
8use std::any::Any;
9use std::{any, fmt};
10
11#[cfg(all(since_api = "4.3", feature = "register-docs"))] #[cfg_attr(published_docs, doc(cfg(all(since_api = "4.3", feature = "register-docs"))))]
12use crate::docs::*;
13use crate::init::InitLevel;
14use crate::meta::ClassName;
15use crate::obj::{bounds, cap, Bounds, DynGd, Gd, GodotClass, Inherits, UserClass};
16use crate::registry::callbacks;
17use crate::registry::class::GodotGetVirtual;
18use crate::{classes, sys};
19
20// TODO(bromeon): some information coming from the proc-macro API is deferred through PluginItem, while others is directly
21// translated to code. Consider moving more code to the PluginItem, which allows for more dynamic registration and will
22// be easier for a future builder API.
23
24// ----------------------------------------------------------------------------------------------------------------------------------------------
25
26/// Piece of information that is gathered by the self-registration ("plugin") system.
27///
28/// You should not manually construct this struct, but rather use [`ClassPlugin::new()`].
29#[derive(Debug)]
30pub struct ClassPlugin {
31    /// The name of the class to register plugins for.
32    ///
33    /// This is used to group plugins so that all class properties for a single class can be registered at the same time.
34    /// Incorrectly setting this value should not cause any UB but will likely cause errors during registration time.
35    pub(crate) class_name: ClassName,
36
37    /// Which [`InitLevel`] this plugin should be registered at.
38    ///
39    /// Incorrectly setting this value should not cause any UB but will likely cause errors during registration time.
40    // Init-level is per ClassPlugin and not per PluginItem, because all components of all classes are mixed together in one
41    // huge linker list. There is no per-class aggregation going on, so this allows to easily filter relevant classes.
42    pub(crate) init_level: InitLevel,
43
44    /// The actual item being registered.
45    pub(crate) item: PluginItem,
46}
47
48impl ClassPlugin {
49    /// Creates a new `ClassPlugin`, automatically setting the `class_name` and `init_level` to the values defined in [`GodotClass`].
50    pub fn new<T: GodotClass>(item: PluginItem) -> Self {
51        Self {
52            class_name: T::class_name(),
53            init_level: T::INIT_LEVEL,
54            item,
55        }
56    }
57}
58
59// ----------------------------------------------------------------------------------------------------------------------------------------------
60// Type-erased values
61
62/// Type-erased function object, holding a function which should called during class registration.
63#[derive(Copy, Clone)]
64pub struct ErasedRegisterFn {
65    // A wrapper is needed here because Debug can't be derived on function pointers with reference parameters, so this won't work:
66    // pub type ErasedRegisterFn = fn(&mut dyn std::any::Any);
67    // (see https://stackoverflow.com/q/53380040)
68    /// The actual function to be called during class registration.
69    pub raw: fn(&mut dyn Any),
70}
71
72impl fmt::Debug for ErasedRegisterFn {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        write!(f, "0x{:0>16x}", self.raw as usize)
75    }
76}
77
78/// Type-erased function object, holding a function which should be called during RPC function registration.
79#[derive(Copy, Clone)]
80pub struct ErasedRegisterRpcsFn {
81    // A wrapper is needed here because Debug can't be derived on function pointers with reference parameters, so this won't work:
82    // pub type ErasedRegisterFn = fn(&mut dyn std::any::Any);
83    // (see https://stackoverflow.com/q/53380040)
84    /// The actual function to be called during RPC function registration.
85    ///
86    /// This should be called with a reference to the object that we want to register RPC functions for.
87    pub raw: fn(&mut dyn Any),
88}
89
90impl fmt::Debug for ErasedRegisterRpcsFn {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        write!(f, "0x{:0>16x}", self.raw as usize)
93    }
94}
95
96/// Type-erased function which converts a `Gd<Object>` into a `DynGd<Object, D>` for some trait object `D`.
97///
98/// See [`DynTraitImpl`] for usage.
99pub type ErasedDynifyFn = unsafe fn(Gd<classes::Object>) -> ErasedDynGd;
100
101/// Type-erased `DynGd<Object, D>` for some trait object `D`.
102///
103/// See [`DynTraitImpl`] for usage.
104pub struct ErasedDynGd {
105    pub boxed: Box<dyn Any>,
106}
107
108type GodotCreateFn = unsafe extern "C" fn(
109    _class_userdata: *mut std::ffi::c_void,
110    #[cfg(since_api = "4.4")] _notify_postinitialize: sys::GDExtensionBool,
111) -> sys::GDExtensionObjectPtr;
112
113// ----------------------------------------------------------------------------------------------------------------------------------------------
114// Plugin items
115
116/// Represents the data part of a [`ClassPlugin`] instance.
117///
118/// Each enumerator represents a different item in Rust code, which is processed by an independent proc macro (for example,
119/// `#[derive(GodotClass)]` on structs, or `#[godot_api]` on impl blocks).
120#[derive(Clone, Debug)]
121pub enum PluginItem {
122    /// Class definition itself, must always be available -- created by `#[derive(GodotClass)]`.
123    Struct(Struct),
124
125    /// Collected from `#[godot_api] impl MyClass`.
126    InherentImpl(InherentImpl),
127
128    /// Collected from `#[godot_api] impl I... for MyClass`.
129    ITraitImpl(ITraitImpl),
130
131    /// Collected from `#[godot_dyn]` macro invocations.
132    DynTraitImpl(DynTraitImpl),
133}
134
135/// Helper function which checks that the field has not been set before.
136fn set<T>(field: &mut Option<T>, value: T) {
137    assert!(field.is_none(), "attempted to set field more than once",);
138    *field = Some(value);
139}
140
141/// The data for a class definition.
142#[derive(Clone, Debug)]
143pub struct Struct {
144    /// The name of the base class in Godot.
145    ///
146    /// This must match [`GodotClass::Base`]'s class name.
147    pub(crate) base_class_name: ClassName,
148
149    /// Godot low-level `create` function, wired up to library-generated `init`.
150    ///
151    /// For `#[class(no_init)]`, behavior depends on Godot version:
152    /// - 4.5 and later: `None`
153    /// - until 4.4: a dummy function that fails, to not break hot reloading.
154    ///
155    /// This is mutually exclusive with [`ITraitImpl::user_create_fn`].
156    pub(crate) generated_create_fn: Option<GodotCreateFn>,
157
158    /// Godot low-level `recreate` function, used when hot-reloading a user class.
159    ///
160    /// This is mutually exclusive with [`ITraitImpl::user_recreate_fn`].
161    pub(crate) generated_recreate_fn: Option<
162        unsafe extern "C" fn(
163            p_class_userdata: *mut std::ffi::c_void,
164            p_object: sys::GDExtensionObjectPtr,
165        ) -> sys::GDExtensionClassInstancePtr,
166    >,
167
168    /// Callback to library-generated function which registers properties in the `struct` definition.
169    pub(crate) register_properties_fn: ErasedRegisterFn,
170
171    /// Callback on refc-increment. Only for `RefCounted` classes.
172    pub(crate) reference_fn: sys::GDExtensionClassReference,
173
174    /// Callback on refc-decrement. Only for `RefCounted` classes.
175    pub(crate) unreference_fn: sys::GDExtensionClassUnreference,
176
177    /// Function called by Godot when an object of this class is freed.
178    ///
179    /// Always implemented as [`callbacks::free`].
180    pub(crate) free_fn: unsafe extern "C" fn(
181        _class_user_data: *mut std::ffi::c_void,
182        instance: sys::GDExtensionClassInstancePtr,
183    ),
184
185    /// Calls `__before_ready()`, if there is at least one `OnReady` field. Used if there is no `#[godot_api] impl` block
186    /// overriding ready.
187    pub(crate) default_get_virtual_fn: Option<GodotGetVirtual>,
188
189    /// Whether `#[class(tool)]` was used.
190    pub(crate) is_tool: bool,
191
192    /// Whether the base class is an `EditorPlugin`.
193    pub(crate) is_editor_plugin: bool,
194
195    /// Whether `#[class(internal)]` was used.
196    pub(crate) is_internal: bool,
197
198    /// Whether the class has a default constructor.
199    pub(crate) is_instantiable: bool,
200
201    /// Documentation extracted from the struct's RustDoc.
202    #[cfg(all(since_api = "4.3", feature = "register-docs"))] #[cfg_attr(published_docs, doc(cfg(all(since_api = "4.3", feature = "register-docs"))))]
203    pub(crate) docs: StructDocs,
204}
205
206impl Struct {
207    pub fn new<T: GodotClass + cap::ImplementsGodotExports>(
208        #[cfg(all(since_api = "4.3", feature = "register-docs"))] #[cfg_attr(published_docs, doc(cfg(all(since_api = "4.3", feature = "register-docs"))))] docs: StructDocs,
209    ) -> Self {
210        let refcounted = <T::Memory as bounds::Memory>::IS_REF_COUNTED;
211
212        Self {
213            base_class_name: T::Base::class_name(),
214            generated_create_fn: None,
215            generated_recreate_fn: None,
216            register_properties_fn: ErasedRegisterFn {
217                raw: callbacks::register_user_properties::<T>,
218            },
219            free_fn: callbacks::free::<T>,
220            default_get_virtual_fn: None,
221            is_tool: false,
222            is_editor_plugin: false,
223            is_internal: false,
224            is_instantiable: false,
225            #[cfg(all(since_api = "4.3", feature = "register-docs"))] #[cfg_attr(published_docs, doc(cfg(all(since_api = "4.3", feature = "register-docs"))))]
226            docs,
227            // While Godot doesn't do anything with these callbacks for non-RefCounted classes, we can avoid instantiating them in Rust.
228            reference_fn: refcounted.then_some(callbacks::reference::<T>),
229            unreference_fn: refcounted.then_some(callbacks::unreference::<T>),
230        }
231    }
232
233    pub fn with_generated<T: GodotClass + cap::GodotDefault>(mut self) -> Self {
234        set(&mut self.generated_create_fn, callbacks::create::<T>);
235
236        #[cfg(since_api = "4.2")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.2")))]
237        set(&mut self.generated_recreate_fn, callbacks::recreate::<T>);
238        self
239    }
240
241    // Workaround for https://github.com/godot-rust/gdext/issues/874, before https://github.com/godotengine/godot/pull/99133 is merged in 4.5.
242    #[cfg(before_api = "4.5")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.5")))]
243    pub fn with_generated_no_default<T: GodotClass>(mut self) -> Self {
244        set(&mut self.generated_create_fn, callbacks::create_null::<T>);
245
246        #[cfg(since_api = "4.2")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.2")))]
247        set(
248            &mut self.generated_recreate_fn,
249            callbacks::recreate_null::<T>,
250        );
251        self
252    }
253
254    pub fn with_default_get_virtual_fn<T: GodotClass + UserClass>(mut self) -> Self {
255        set(
256            &mut self.default_get_virtual_fn,
257            callbacks::default_get_virtual::<T>,
258        );
259        self
260    }
261
262    pub fn with_tool(mut self) -> Self {
263        self.is_tool = true;
264        self
265    }
266
267    pub fn with_editor_plugin(mut self) -> Self {
268        self.is_editor_plugin = true;
269        self
270    }
271
272    pub fn with_internal(mut self) -> Self {
273        self.is_internal = true;
274        self
275    }
276
277    pub fn with_instantiable(mut self) -> Self {
278        self.is_instantiable = true;
279        self
280    }
281}
282
283/// Stores registration functions for methods, constants, and documentation from inherent `#[godot_api]` impl blocks.
284#[derive(Clone, Debug)]
285pub struct InherentImpl {
286    /// Callback to library-generated function which registers functions and constants in the `impl` block.
287    ///
288    /// Always present since that's the entire point of this `impl` block.
289    pub(crate) register_methods_constants_fn: ErasedRegisterFn,
290
291    /// Callback to library-generated function which calls [`Node::rpc_config`](crate::classes::Node::rpc_config) for each function annotated
292    /// with `#[rpc]` on the `impl` block.
293    ///
294    /// This function is called in [`UserClass::__before_ready()`](crate::obj::UserClass::__before_ready) definitions generated by the
295    /// `#[derive(GodotClass)]` macro.
296    // This field is only used during codegen-full.
297    #[cfg_attr(not(feature = "codegen-full"), expect(dead_code))]
298    pub(crate) register_rpcs_fn: Option<ErasedRegisterRpcsFn>,
299
300    #[cfg(all(since_api = "4.3", feature = "register-docs"))] #[cfg_attr(published_docs, doc(cfg(all(since_api = "4.3", feature = "register-docs"))))]
301    pub docs: InherentImplDocs,
302}
303
304impl InherentImpl {
305    pub fn new<T: cap::ImplementsGodotApi>(
306        #[cfg(all(since_api = "4.3", feature = "register-docs"))] #[cfg_attr(published_docs, doc(cfg(all(since_api = "4.3", feature = "register-docs"))))] docs: InherentImplDocs,
307    ) -> Self {
308        Self {
309            register_methods_constants_fn: ErasedRegisterFn {
310                raw: callbacks::register_user_methods_constants::<T>,
311            },
312            register_rpcs_fn: Some(ErasedRegisterRpcsFn {
313                raw: callbacks::register_user_rpcs::<T>,
314            }),
315            #[cfg(all(since_api = "4.3", feature = "register-docs"))] #[cfg_attr(published_docs, doc(cfg(all(since_api = "4.3", feature = "register-docs"))))]
316            docs,
317        }
318    }
319}
320
321#[derive(Default, Clone, Debug)]
322pub struct ITraitImpl {
323    #[cfg(all(since_api = "4.3", feature = "register-docs"))]
324    /// Virtual method documentation.
325    pub(crate) virtual_method_docs: &'static str,
326
327    /// Callback to user-defined `register_class` function.
328    pub(crate) user_register_fn: Option<ErasedRegisterFn>,
329
330    /// Godot low-level `create` function, wired up to the user's `init`.
331    ///
332    /// This is mutually exclusive with [`Struct::generated_create_fn`].
333    pub(crate) user_create_fn: Option<GodotCreateFn>,
334
335    /// Godot low-level `recreate` function, used when hot-reloading a user class.
336    ///
337    /// This is mutually exclusive with [`Struct::generated_recreate_fn`].
338    pub(crate) user_recreate_fn: Option<
339        unsafe extern "C" fn(
340            p_class_userdata: *mut ::std::os::raw::c_void,
341            p_object: sys::GDExtensionObjectPtr,
342        ) -> sys::GDExtensionClassInstancePtr,
343    >,
344
345    /// User-defined `to_string` function.
346    pub(crate) user_to_string_fn: Option<
347        unsafe extern "C" fn(
348            p_instance: sys::GDExtensionClassInstancePtr,
349            r_is_valid: *mut sys::GDExtensionBool,
350            r_out: sys::GDExtensionStringPtr,
351        ),
352    >,
353
354    /// User-defined `on_notification` function.
355    #[cfg(before_api = "4.2")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.2")))]
356    pub(crate) user_on_notification_fn:
357        Option<unsafe extern "C" fn(p_instance: sys::GDExtensionClassInstancePtr, p_what: i32)>,
358    #[cfg(since_api = "4.2")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.2")))]
359    pub(crate) user_on_notification_fn: Option<
360        unsafe extern "C" fn(
361            p_instance: sys::GDExtensionClassInstancePtr,
362            p_what: i32,
363            p_reversed: sys::GDExtensionBool,
364        ),
365    >,
366
367    /// User-defined `set_property` function.
368    pub(crate) user_set_fn: Option<
369        unsafe extern "C" fn(
370            p_instance: sys::GDExtensionClassInstancePtr,
371            p_name: sys::GDExtensionConstStringNamePtr,
372            p_value: sys::GDExtensionConstVariantPtr,
373        ) -> sys::GDExtensionBool,
374    >,
375
376    /// User-defined `get_property` function.
377    pub(crate) user_get_fn: Option<
378        unsafe extern "C" fn(
379            p_instance: sys::GDExtensionClassInstancePtr,
380            p_name: sys::GDExtensionConstStringNamePtr,
381            r_ret: sys::GDExtensionVariantPtr,
382        ) -> sys::GDExtensionBool,
383    >,
384
385    /// Callback for other virtual methods specific to each class.
386    pub(crate) get_virtual_fn: Option<GodotGetVirtual>,
387
388    /// User-defined `get_property_list` function.
389    pub(crate) user_get_property_list_fn: Option<
390        unsafe extern "C" fn(
391            p_instance: sys::GDExtensionClassInstancePtr,
392            r_count: *mut u32,
393        ) -> *const sys::GDExtensionPropertyInfo,
394    >,
395
396    // We do not support using this in Godot < 4.3, however it's easier to leave this in and fail elsewhere when attempting to use
397    // this in Godot < 4.3.
398    #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
399    pub(crate) user_free_property_list_fn: Option<
400        unsafe extern "C" fn(
401            p_instance: sys::GDExtensionClassInstancePtr,
402            p_list: *const sys::GDExtensionPropertyInfo,
403        ),
404    >,
405    /// Frees the property list created in the user-defined `get_property_list` function.
406    #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
407    pub(crate) user_free_property_list_fn: Option<
408        unsafe extern "C" fn(
409            p_instance: sys::GDExtensionClassInstancePtr,
410            p_list: *const sys::GDExtensionPropertyInfo,
411            p_count: u32,
412        ),
413    >,
414
415    /// Part of user-defined `property_get_revert` function.
416    ///
417    /// This effectively just calls [`Option::is_some`] on the return value of the `property_get_revert` function.
418    pub(crate) user_property_can_revert_fn: Option<
419        unsafe extern "C" fn(
420            p_instance: sys::GDExtensionClassInstancePtr,
421            p_name: sys::GDExtensionConstStringNamePtr,
422        ) -> sys::GDExtensionBool,
423    >,
424
425    /// Part of user-defined `property_get_revert` function.
426    ///
427    /// This returns null when the return value of `property_get_revert` is `None`, and otherwise returns the value contained
428    /// within the `Some`.
429    pub(crate) user_property_get_revert_fn: Option<
430        unsafe extern "C" fn(
431            p_instance: sys::GDExtensionClassInstancePtr,
432            p_name: sys::GDExtensionConstStringNamePtr,
433            r_ret: sys::GDExtensionVariantPtr,
434        ) -> sys::GDExtensionBool,
435    >,
436    #[cfg(since_api = "4.2")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.2")))]
437    pub(crate) validate_property_fn: Option<
438        unsafe extern "C" fn(
439            p_instance: sys::GDExtensionClassInstancePtr,
440            p_property: *mut sys::GDExtensionPropertyInfo,
441        ) -> sys::GDExtensionBool,
442    >,
443}
444
445impl ITraitImpl {
446    pub fn new<T: GodotClass + cap::ImplementsGodotVirtual>(
447        #[cfg(all(since_api = "4.3", feature = "register-docs"))] #[cfg_attr(published_docs, doc(cfg(all(since_api = "4.3", feature = "register-docs"))))] virtual_method_docs: &'static str,
448    ) -> Self {
449        Self {
450            #[cfg(all(since_api = "4.3", feature = "register-docs"))] #[cfg_attr(published_docs, doc(cfg(all(since_api = "4.3", feature = "register-docs"))))]
451            virtual_method_docs,
452            get_virtual_fn: Some(callbacks::get_virtual::<T>),
453            ..Default::default()
454        }
455    }
456
457    pub fn with_register<T: GodotClass + cap::GodotRegisterClass>(mut self) -> Self {
458        set(
459            &mut self.user_register_fn,
460            ErasedRegisterFn {
461                raw: callbacks::register_class_by_builder::<T>,
462            },
463        );
464
465        self
466    }
467
468    pub fn with_create<T: GodotClass + cap::GodotDefault>(mut self) -> Self {
469        set(&mut self.user_create_fn, callbacks::create::<T>);
470
471        #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
472        set(&mut self.user_recreate_fn, callbacks::recreate::<T>);
473        self
474    }
475
476    pub fn with_string<T: GodotClass + cap::GodotToString>(mut self) -> Self {
477        set(&mut self.user_to_string_fn, callbacks::to_string::<T>);
478        self
479    }
480
481    pub fn with_on_notification<T: GodotClass + cap::GodotNotification>(mut self) -> Self {
482        set(
483            &mut self.user_on_notification_fn,
484            callbacks::on_notification::<T>,
485        );
486        self
487    }
488
489    pub fn with_get_property<T: GodotClass + cap::GodotGet>(mut self) -> Self {
490        set(&mut self.user_get_fn, callbacks::get_property::<T>);
491        self
492    }
493
494    pub fn with_set_property<T: GodotClass + cap::GodotSet>(mut self) -> Self {
495        set(&mut self.user_set_fn, callbacks::set_property::<T>);
496        self
497    }
498
499    pub fn with_get_property_list<T: GodotClass + cap::GodotGetPropertyList>(mut self) -> Self {
500        set(
501            &mut self.user_get_property_list_fn,
502            callbacks::get_property_list::<T>,
503        );
504
505        #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
506        set(
507            &mut self.user_free_property_list_fn,
508            callbacks::free_property_list::<T>,
509        );
510        self
511    }
512
513    pub fn with_property_get_revert<T: GodotClass + cap::GodotPropertyGetRevert>(mut self) -> Self {
514        set(
515            &mut self.user_property_get_revert_fn,
516            callbacks::property_get_revert::<T>,
517        );
518        set(
519            &mut self.user_property_can_revert_fn,
520            callbacks::property_can_revert::<T>,
521        );
522        self
523    }
524
525    #[cfg(since_api = "4.2")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.2")))]
526    pub fn with_validate_property<T: GodotClass + cap::GodotValidateProperty>(mut self) -> Self {
527        set(
528            &mut self.validate_property_fn,
529            callbacks::validate_property::<T>,
530        );
531        self
532    }
533}
534
535/// Representation of a `#[godot_dyn]` invocation.
536///
537/// Stores all the information needed for `DynGd` re-enrichment.
538#[derive(Clone, Debug)]
539pub struct DynTraitImpl {
540    /// The class that this `dyn Trait` implementation corresponds to.
541    class_name: ClassName,
542
543    /// Base inherited class required for `DynGd<T, D>` exports (i.e. one specified in `#[class(base = ...)]`).
544    ///
545    /// Godot doesn't guarantee availability of all the GDExtension classes through the ClassDb while generating `PropertyHintInfo` for our exports.
546    /// Therefore, we rely on the built-in inherited base class in such cases.
547    /// Only [`class_name`][DynTraitImpl::class_name] is available at the time of adding given `DynTraitImpl` to plugin registry with `#[godot_dyn]`;
548    /// It is important to fill this information before registration.
549    ///
550    /// See also [`get_dyn_property_hint_string`][crate::registry::class::get_dyn_property_hint_string].
551    pub(crate) parent_class_name: Option<ClassName>,
552
553    /// TypeId of the `dyn Trait` object.
554    dyn_trait_typeid: any::TypeId,
555
556    /// Function used to get a `DynGd<T,D>` from a `Gd<Object>`. This is used in the [`FromGodot`](crate::meta::FromGodot) implementation
557    /// of [`DynGd`]. This function is always implemented as [`callbacks::dynify_fn::<T, D>`] where `T` is the class represented by `class_name`
558    /// and `D` is the trait object corresponding to `dyn_trait_typeid`.
559    ///
560    /// Function that converts a `Gd<Object>` to a type-erased `DynGd<Object, dyn Trait>` (with the latter erased for common storage).
561    erased_dynify_fn: ErasedDynifyFn,
562}
563
564impl DynTraitImpl {
565    pub fn new<T, D>() -> Self
566    where
567        T: GodotClass
568            + Inherits<classes::Object>
569            + crate::obj::AsDyn<D>
570            + Bounds<Declarer = bounds::DeclUser>,
571        D: ?Sized + 'static,
572    {
573        Self {
574            class_name: T::class_name(),
575            parent_class_name: None,
576            dyn_trait_typeid: std::any::TypeId::of::<D>(),
577            erased_dynify_fn: callbacks::dynify_fn::<T, D>,
578        }
579    }
580
581    /// The class that this `dyn Trait` implementation corresponds to.
582    pub fn class_name(&self) -> &ClassName {
583        &self.class_name
584    }
585
586    /// The type id of the trait object this was registered with.
587    pub fn dyn_trait_typeid(&self) -> any::TypeId {
588        self.dyn_trait_typeid
589    }
590
591    /// Convert a [`Gd<T>`] to a [`DynGd<T, D>`] using `self`.
592    ///
593    /// This will fail with `Err(object)` if the dynamic class of `object` does not match the [`ClassName`] stored in `self`.
594    pub fn get_dyn_gd<T: GodotClass, D: ?Sized + 'static>(
595        &self,
596        object: Gd<T>,
597    ) -> Result<DynGd<T, D>, Gd<T>> {
598        let dynamic_class = object.dynamic_class_string();
599
600        if dynamic_class != self.class_name.to_string_name() {
601            return Err(object);
602        }
603
604        let object = object.upcast_object();
605
606        // SAFETY: `DynTraitImpl::new` ensures that this function is safe to call when `object` is castable to `self.class_name`.
607        // Since the dynamic class of `object` is `self.class_name`, it must be castable to `self.class_name`.
608        let erased_dyn = unsafe { (self.erased_dynify_fn)(object) };
609
610        let dyn_gd_object = erased_dyn.boxed.downcast::<DynGd<classes::Object, D>>();
611
612        // SAFETY: `callbacks::dynify_fn` returns a `DynGd<Object, D>` which has been type erased. So this downcast will always succeed.
613        let dyn_gd_object = unsafe { dyn_gd_object.unwrap_unchecked() };
614
615        // SAFETY: This is effectively upcasting a value which has class equal to `self.class_name` to a `DynGd<T, D>`. Since the class of
616        // `object` is `T` and its dynamic class is `self.class_name`, this means that `T` must be a superclass of `self.class_name`. Thus
617        // this upcast is safe.
618        let dyn_gd_t = unsafe { dyn_gd_object.cast_unchecked::<T>() };
619
620        Ok(dyn_gd_t)
621    }
622}