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