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
11use crate::init::InitLevel;
12use crate::meta::ClassId;
13use crate::obj::{
14 bounds, cap, Bounds, DynGd, Gd, GodotClass, Inherits, NewAlloc, Singleton, UserClass,
15 UserSingleton,
16};
17use crate::registry::callbacks;
18use crate::registry::class::GodotGetVirtual;
19use crate::{classes, sys};
20
21// TODO(bromeon): some information coming from the proc-macro API is deferred through PluginItem, while others is directly
22// translated to code. Consider moving more code to the PluginItem, which allows for more dynamic registration and will
23// be easier for a future builder API.
24
25// ----------------------------------------------------------------------------------------------------------------------------------------------
26
27/// Piece of information that is gathered by the self-registration ("plugin") system.
28///
29/// You should not manually construct this struct, but rather use [`ClassPlugin::new()`].
30#[derive(Debug)]
31pub struct ClassPlugin {
32 /// The name of the class to register plugins for.
33 ///
34 /// This is used to group plugins so that all class properties for a single class can be registered at the same time.
35 /// Incorrectly setting this value should not cause any UB but will likely cause errors during registration time.
36 pub(crate) class_name: ClassId,
37
38 /// Which [`InitLevel`] this plugin should be registered at.
39 ///
40 /// Incorrectly setting this value should not cause any UB but will likely cause errors during registration time.
41 // Init-level is per ClassPlugin and not per PluginItem, because all components of all classes are mixed together in one
42 // huge linker list. There is no per-class aggregation going on, so this allows to easily filter relevant classes.
43 pub(crate) init_level: InitLevel,
44
45 /// The actual item being registered.
46 pub(crate) item: PluginItem,
47}
48
49impl ClassPlugin {
50 /// Creates a new `ClassPlugin`, automatically setting the `class_name` and `init_level` to the values defined in [`GodotClass`].
51 pub fn new<T: GodotClass>(item: PluginItem) -> Self {
52 Self {
53 class_name: T::class_id(),
54 init_level: T::INIT_LEVEL,
55 item,
56 }
57 }
58}
59
60// ----------------------------------------------------------------------------------------------------------------------------------------------
61// Type-erased values
62
63/// Type-erased function object, holding a function which should called during class registration.
64#[derive(Copy, Clone)]
65pub struct ErasedRegisterFn {
66 // A wrapper is needed here because Debug can't be derived on function pointers with reference parameters, so this won't work:
67 // pub type ErasedRegisterFn = fn(&mut dyn std::any::Any);
68 // (see https://stackoverflow.com/q/53380040)
69 /// The actual function to be called during class registration.
70 pub raw: fn(&mut dyn Any),
71}
72
73impl fmt::Debug for ErasedRegisterFn {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 write!(f, "0x{:0>16x}", self.raw as usize)
76 }
77}
78
79/// Type-erased function object, holding a function which should be called during RPC function registration.
80#[derive(Copy, Clone)]
81pub struct ErasedRegisterRpcsFn {
82 // A wrapper is needed here because Debug can't be derived on function pointers with reference parameters, so this won't work:
83 // pub type ErasedRegisterFn = fn(&mut dyn std::any::Any);
84 // (see https://stackoverflow.com/q/53380040)
85 /// The actual function to be called during RPC function registration.
86 ///
87 /// This should be called with a reference to the object that we want to register RPC functions for.
88 pub raw: fn(&mut dyn Any),
89}
90
91impl fmt::Debug for ErasedRegisterRpcsFn {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 write!(f, "0x{:0>16x}", self.raw as usize)
94 }
95}
96
97/// Type-erased function which converts a `Gd<Object>` into a `DynGd<Object, D>` for some trait object `D`.
98///
99/// See [`DynTraitImpl`] for usage.
100pub type ErasedDynifyFn = unsafe fn(Gd<classes::Object>) -> ErasedDynGd;
101
102/// Type-erased `DynGd<Object, D>` for some trait object `D`.
103///
104/// See [`DynTraitImpl`] for usage.
105pub struct ErasedDynGd {
106 pub boxed: Box<dyn Any>,
107}
108
109type GodotCreateFn = unsafe extern "C" fn(
110 _class_userdata: *mut std::ffi::c_void,
111 #[cfg(since_api = "4.4")] _notify_postinitialize: sys::GDExtensionBool,
112) -> sys::GDExtensionObjectPtr;
113
114// ----------------------------------------------------------------------------------------------------------------------------------------------
115// Plugin items
116
117/// Represents the data part of a [`ClassPlugin`] instance.
118///
119/// Each enumerator represents a different item in Rust code, which is processed by an independent proc macro (for example,
120/// `#[derive(GodotClass)]` on structs, or `#[godot_api]` on impl blocks).
121#[derive(Clone, Debug)]
122pub enum PluginItem {
123 /// Class definition itself, must always be available -- created by `#[derive(GodotClass)]`.
124 Struct(Struct),
125
126 /// Collected from `#[godot_api] impl MyClass`.
127 InherentImpl(InherentImpl),
128
129 /// Collected from `#[godot_api] impl I... for MyClass`.
130 ITraitImpl(ITraitImpl),
131
132 /// Collected from `#[godot_dyn]` macro invocations.
133 DynTraitImpl(DynTraitImpl),
134}
135
136/// Helper function which checks that the field has not been set before.
137fn set<T>(field: &mut Option<T>, value: T) {
138 assert!(field.is_none(), "attempted to set field more than once",);
139 *field = Some(value);
140}
141
142/// The data for a class definition.
143#[derive(Clone, Debug)]
144pub struct Struct {
145 /// The name of the base class in Godot.
146 ///
147 /// This must match [`GodotClass::Base`]'s class name.
148 pub(crate) base_class_name: ClassId,
149
150 /// Godot low-level `create` function, wired up to library-generated `init`.
151 ///
152 /// For `#[class(no_init)]`, behavior depends on Godot version:
153 /// - 4.5 and later: `None`
154 /// - until 4.4: a dummy function that fails, to not break hot reloading.
155 ///
156 /// This is mutually exclusive with [`ITraitImpl::user_create_fn`].
157 pub(crate) generated_create_fn: Option<GodotCreateFn>,
158
159 /// Godot low-level `recreate` function, used when hot-reloading a user class.
160 ///
161 /// This is mutually exclusive with [`ITraitImpl::user_recreate_fn`].
162 pub(crate) generated_recreate_fn: Option<
163 unsafe extern "C" fn(
164 p_class_userdata: *mut std::ffi::c_void,
165 p_object: sys::GDExtensionObjectPtr,
166 ) -> sys::GDExtensionClassInstancePtr,
167 >,
168
169 /// Callback to library-generated function which registers properties in the `struct` definition.
170 pub(crate) register_properties_fn: ErasedRegisterFn,
171
172 /// Callback on refc-increment. Only for `RefCounted` classes.
173 pub(crate) reference_fn: sys::GDExtensionClassReference,
174
175 /// Callback on refc-decrement. Only for `RefCounted` classes.
176 pub(crate) unreference_fn: sys::GDExtensionClassUnreference,
177
178 /// Function called by Godot when an object of this class is freed.
179 ///
180 /// Always implemented as [`callbacks::free`].
181 pub(crate) free_fn: unsafe extern "C" fn(
182 _class_user_data: *mut std::ffi::c_void,
183 instance: sys::GDExtensionClassInstancePtr,
184 ),
185
186 /// `#[class(singleton)]`
187 pub(crate) register_singleton_fn: Option<fn()>,
188
189 /// `#[class(singleton)]`
190 pub(crate) unregister_singleton_fn: Option<fn()>,
191
192 /// Calls `__before_ready()`, if there is at least one `OnReady` field. Used if there is no `#[godot_api] impl` block
193 /// overriding ready.
194 pub(crate) default_get_virtual_fn: Option<GodotGetVirtual>,
195
196 /// Whether `#[class(tool)]` was used.
197 pub(crate) is_tool: bool,
198
199 /// Whether the base class is an `EditorPlugin`.
200 pub(crate) is_editor_plugin: bool,
201
202 /// Whether `#[class(internal)]` was used.
203 pub(crate) is_internal: bool,
204
205 /// Whether the class has a default constructor.
206 pub(crate) is_instantiable: bool,
207}
208
209impl Struct {
210 pub fn new<T: GodotClass + cap::ImplementsGodotExports>() -> Self {
211 let refcounted = <T::Memory as bounds::Memory>::IS_REF_COUNTED;
212
213 Self {
214 base_class_name: T::Base::class_id(),
215 generated_create_fn: None,
216 generated_recreate_fn: None,
217 register_properties_fn: ErasedRegisterFn {
218 raw: callbacks::register_user_properties::<T>,
219 },
220 free_fn: callbacks::free::<T>,
221 register_singleton_fn: None,
222 unregister_singleton_fn: None,
223 default_get_virtual_fn: None,
224 is_tool: false,
225 is_editor_plugin: false,
226 is_internal: false,
227 is_instantiable: false,
228 // While Godot doesn't do anything with these callbacks for non-RefCounted classes, we can avoid instantiating them in Rust.
229 reference_fn: refcounted.then_some(callbacks::reference::<T>),
230 unreference_fn: refcounted.then_some(callbacks::unreference::<T>),
231 }
232 }
233
234 pub fn with_generated<T: GodotClass + cap::GodotDefault>(mut self) -> Self {
235 set(&mut self.generated_create_fn, callbacks::create::<T>);
236
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 set(
247 &mut self.generated_recreate_fn,
248 callbacks::recreate_null::<T>,
249 );
250 self
251 }
252
253 pub fn with_default_get_virtual_fn<T: GodotClass + UserClass>(mut self) -> Self {
254 set(
255 &mut self.default_get_virtual_fn,
256 callbacks::default_get_virtual::<T>,
257 );
258 self
259 }
260
261 pub fn with_tool(mut self) -> Self {
262 self.is_tool = true;
263 self
264 }
265
266 pub fn with_editor_plugin(mut self) -> Self {
267 self.is_editor_plugin = true;
268 self
269 }
270
271 pub fn with_singleton<T>(mut self) -> Self
272 where
273 T: UserSingleton
274 + Bounds<Memory = bounds::MemManual, Declarer = bounds::DeclUser>
275 + NewAlloc
276 + Inherits<classes::Object>,
277 {
278 self.register_singleton_fn = Some(|| {
279 crate::classes::Engine::singleton()
280 .register_singleton(&T::class_id().to_string_name(), &T::new_alloc());
281 });
282
283 self.unregister_singleton_fn = Some(|| {
284 let singleton = T::singleton();
285 crate::classes::Engine::singleton()
286 .unregister_singleton(&T::class_id().to_string_name());
287 singleton.free();
288 });
289
290 self
291 }
292
293 pub fn with_internal(mut self) -> Self {
294 self.is_internal = true;
295 self
296 }
297
298 pub fn with_instantiable(mut self) -> Self {
299 self.is_instantiable = true;
300 self
301 }
302}
303
304/// Stores registration functions for methods, constants, and documentation from inherent `#[godot_api]` impl blocks.
305#[derive(Clone, Debug)]
306pub struct InherentImpl {
307 /// Callback to library-generated function which registers functions and constants in the `impl` block.
308 ///
309 /// Always present since that's the entire point of this `impl` block.
310 pub(crate) register_methods_constants_fn: ErasedRegisterFn,
311
312 /// Callback to library-generated function which calls [`Node::rpc_config`](crate::classes::Node::rpc_config) for each function annotated
313 /// with `#[rpc]` on the `impl` block.
314 ///
315 /// This function is called in [`UserClass::__before_ready()`](crate::obj::UserClass::__before_ready) definitions generated by the
316 /// `#[derive(GodotClass)]` macro.
317 // This field is only used during codegen-full.
318 #[cfg_attr(not(feature = "codegen-full"), expect(dead_code))]
319 pub(crate) register_rpcs_fn: Option<ErasedRegisterRpcsFn>,
320}
321
322impl InherentImpl {
323 pub fn new<T: cap::ImplementsGodotApi>() -> Self {
324 Self {
325 register_methods_constants_fn: ErasedRegisterFn {
326 raw: callbacks::register_user_methods_constants::<T>,
327 },
328 register_rpcs_fn: Some(ErasedRegisterRpcsFn {
329 raw: callbacks::register_user_rpcs::<T>,
330 }),
331 }
332 }
333}
334
335#[derive(Default, Clone, Debug)]
336pub struct ITraitImpl {
337 /// Callback to user-defined `register_class` function.
338 pub(crate) user_register_fn: Option<ErasedRegisterFn>,
339
340 /// Godot low-level `create` function, wired up to the user's `init`.
341 ///
342 /// This is mutually exclusive with [`Struct::generated_create_fn`].
343 pub(crate) user_create_fn: Option<GodotCreateFn>,
344
345 /// Godot low-level `recreate` function, used when hot-reloading a user class.
346 ///
347 /// This is mutually exclusive with [`Struct::generated_recreate_fn`].
348 pub(crate) user_recreate_fn: Option<
349 unsafe extern "C" fn(
350 p_class_userdata: *mut ::std::os::raw::c_void,
351 p_object: sys::GDExtensionObjectPtr,
352 ) -> sys::GDExtensionClassInstancePtr,
353 >,
354
355 /// User-defined `to_string` function.
356 pub(crate) user_to_string_fn: Option<
357 unsafe extern "C" fn(
358 p_instance: sys::GDExtensionClassInstancePtr,
359 r_is_valid: *mut sys::GDExtensionBool,
360 r_out: sys::GDExtensionStringPtr,
361 ),
362 >,
363
364 /// User-defined `on_notification` function.
365 pub(crate) user_on_notification_fn: Option<
366 unsafe extern "C" fn(
367 p_instance: sys::GDExtensionClassInstancePtr,
368 p_what: i32,
369 p_reversed: sys::GDExtensionBool,
370 ),
371 >,
372
373 /// User-defined `set_property` function.
374 pub(crate) user_set_fn: Option<
375 unsafe extern "C" fn(
376 p_instance: sys::GDExtensionClassInstancePtr,
377 p_name: sys::GDExtensionConstStringNamePtr,
378 p_value: sys::GDExtensionConstVariantPtr,
379 ) -> sys::GDExtensionBool,
380 >,
381
382 /// User-defined `get_property` function.
383 pub(crate) user_get_fn: Option<
384 unsafe extern "C" fn(
385 p_instance: sys::GDExtensionClassInstancePtr,
386 p_name: sys::GDExtensionConstStringNamePtr,
387 r_ret: sys::GDExtensionVariantPtr,
388 ) -> sys::GDExtensionBool,
389 >,
390
391 /// Callback for other virtual methods specific to each class.
392 pub(crate) get_virtual_fn: Option<GodotGetVirtual>,
393
394 /// User-defined `get_property_list` function.
395 pub(crate) user_get_property_list_fn: Option<
396 unsafe extern "C" fn(
397 p_instance: sys::GDExtensionClassInstancePtr,
398 r_count: *mut u32,
399 ) -> *const sys::GDExtensionPropertyInfo,
400 >,
401
402 // 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
403 // this in Godot < 4.3.
404 #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
405 pub(crate) user_free_property_list_fn: Option<
406 unsafe extern "C" fn(
407 p_instance: sys::GDExtensionClassInstancePtr,
408 p_list: *const sys::GDExtensionPropertyInfo,
409 ),
410 >,
411 /// Frees the property list created in the user-defined `get_property_list` function.
412 #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
413 pub(crate) user_free_property_list_fn: Option<
414 unsafe extern "C" fn(
415 p_instance: sys::GDExtensionClassInstancePtr,
416 p_list: *const sys::GDExtensionPropertyInfo,
417 p_count: u32,
418 ),
419 >,
420
421 /// Part of user-defined `property_get_revert` function.
422 ///
423 /// This effectively just calls [`Option::is_some`] on the return value of the `property_get_revert` function.
424 pub(crate) user_property_can_revert_fn: Option<
425 unsafe extern "C" fn(
426 p_instance: sys::GDExtensionClassInstancePtr,
427 p_name: sys::GDExtensionConstStringNamePtr,
428 ) -> sys::GDExtensionBool,
429 >,
430
431 /// Part of user-defined `property_get_revert` function.
432 ///
433 /// This returns null when the return value of `property_get_revert` is `None`, and otherwise returns the value contained
434 /// within the `Some`.
435 pub(crate) user_property_get_revert_fn: Option<
436 unsafe extern "C" fn(
437 p_instance: sys::GDExtensionClassInstancePtr,
438 p_name: sys::GDExtensionConstStringNamePtr,
439 r_ret: sys::GDExtensionVariantPtr,
440 ) -> sys::GDExtensionBool,
441 >,
442 pub(crate) validate_property_fn: Option<
443 unsafe extern "C" fn(
444 p_instance: sys::GDExtensionClassInstancePtr,
445 p_property: *mut sys::GDExtensionPropertyInfo,
446 ) -> sys::GDExtensionBool,
447 >,
448}
449
450impl ITraitImpl {
451 pub fn new<T: GodotClass + cap::ImplementsGodotVirtual>() -> Self {
452 Self {
453 get_virtual_fn: Some(callbacks::get_virtual::<T>),
454 ..Default::default()
455 }
456 }
457
458 pub fn with_register<T: GodotClass + cap::GodotRegisterClass>(mut self) -> Self {
459 set(
460 &mut self.user_register_fn,
461 ErasedRegisterFn {
462 raw: callbacks::register_class_by_builder::<T>,
463 },
464 );
465
466 self
467 }
468
469 pub fn with_create<T: GodotClass + cap::GodotDefault>(mut self) -> Self {
470 set(&mut self.user_create_fn, callbacks::create::<T>);
471
472 #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
473 set(&mut self.user_recreate_fn, callbacks::recreate::<T>);
474 self
475 }
476
477 pub fn with_string<T: GodotClass + cap::GodotToString>(mut self) -> Self {
478 set(&mut self.user_to_string_fn, callbacks::to_string::<T>);
479 self
480 }
481
482 pub fn with_on_notification<T: GodotClass + cap::GodotNotification>(mut self) -> Self {
483 set(
484 &mut self.user_on_notification_fn,
485 callbacks::on_notification::<T>,
486 );
487 self
488 }
489
490 pub fn with_get_property<T: GodotClass + cap::GodotGet>(mut self) -> Self {
491 set(&mut self.user_get_fn, callbacks::get_property::<T>);
492 self
493 }
494
495 pub fn with_set_property<T: GodotClass + cap::GodotSet>(mut self) -> Self {
496 set(&mut self.user_set_fn, callbacks::set_property::<T>);
497 self
498 }
499
500 pub fn with_get_property_list<T: GodotClass + cap::GodotGetPropertyList>(mut self) -> Self {
501 set(
502 &mut self.user_get_property_list_fn,
503 callbacks::get_property_list::<T>,
504 );
505
506 #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
507 set(
508 &mut self.user_free_property_list_fn,
509 callbacks::free_property_list::<T>,
510 );
511 self
512 }
513
514 pub fn with_property_get_revert<T: GodotClass + cap::GodotPropertyGetRevert>(mut self) -> Self {
515 set(
516 &mut self.user_property_get_revert_fn,
517 callbacks::property_get_revert::<T>,
518 );
519 set(
520 &mut self.user_property_can_revert_fn,
521 callbacks::property_can_revert::<T>,
522 );
523 self
524 }
525
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: ClassId,
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<ClassId>,
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_id(),
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) -> &ClassId {
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 [`ClassId`] 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}