1use std::collections::HashMap;
9use std::{any, ptr};
10
11use godot_ffi::join_with;
12use sys::{interface_fn, out, Global, GlobalGuard, GlobalLockError};
13
14use crate::classes::ClassDb;
15use crate::init::InitLevel;
16use crate::meta::error::FromGodotError;
17use crate::meta::ClassName;
18use crate::obj::{cap, DynGd, Gd, GodotClass};
19use crate::private::{ClassPlugin, PluginItem};
20use crate::registry::callbacks;
21use crate::registry::plugin::{DynTraitImpl, ErasedRegisterFn, ITraitImpl, InherentImpl, Struct};
22use crate::{classes, godot_error, godot_warn, sys};
23
24fn global_loaded_classes_by_init_level(
31) -> GlobalGuard<'static, HashMap<InitLevel, Vec<LoadedClass>>> {
32 static LOADED_CLASSES_BY_INIT_LEVEL: Global<
33 HashMap<InitLevel, Vec<LoadedClass>>, > = Global::default();
35
36 lock_or_panic(&LOADED_CLASSES_BY_INIT_LEVEL, "loaded classes")
37}
38
39fn global_loaded_classes_by_name() -> GlobalGuard<'static, HashMap<ClassName, ClassMetadata>> {
44 static LOADED_CLASSES_BY_NAME: Global<HashMap<ClassName, ClassMetadata>> = Global::default();
45
46 lock_or_panic(&LOADED_CLASSES_BY_NAME, "loaded classes (by name)")
47}
48
49fn global_dyn_traits_by_typeid() -> GlobalGuard<'static, HashMap<any::TypeId, Vec<DynTraitImpl>>> {
50 static DYN_TRAITS_BY_TYPEID: Global<HashMap<any::TypeId, Vec<DynTraitImpl>>> =
51 Global::default();
52
53 lock_or_panic(&DYN_TRAITS_BY_TYPEID, "dyn traits")
54}
55
56pub struct LoadedClass {
62 name: ClassName,
63 is_editor_plugin: bool,
64}
65
66pub struct ClassMetadata {}
70
71#[cfg(before_api = "4.2")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.2")))]
75type GodotCreationInfo = sys::GDExtensionClassCreationInfo;
76#[cfg(all(since_api = "4.2", before_api = "4.3"))] #[cfg_attr(published_docs, doc(cfg(all(since_api = "4.2", before_api = "4.3"))))]
77type GodotCreationInfo = sys::GDExtensionClassCreationInfo2;
78#[cfg(all(since_api = "4.3", before_api = "4.4"))] #[cfg_attr(published_docs, doc(cfg(all(since_api = "4.3", before_api = "4.4"))))]
79type GodotCreationInfo = sys::GDExtensionClassCreationInfo3;
80#[cfg(since_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.4")))]
81type GodotCreationInfo = sys::GDExtensionClassCreationInfo4;
82
83#[cfg(before_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.4")))]
84pub(crate) type GodotGetVirtual = <sys::GDExtensionClassGetVirtual as sys::Inner>::FnPtr;
85#[cfg(since_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.4")))]
86pub(crate) type GodotGetVirtual = <sys::GDExtensionClassGetVirtual2 as sys::Inner>::FnPtr;
87
88#[derive(Debug)]
89struct ClassRegistrationInfo {
90 class_name: ClassName,
91 parent_class_name: Option<ClassName>,
92 register_methods_constants_fn: Option<ErasedRegisterFn>,
94 register_properties_fn: Option<ErasedRegisterFn>,
95 user_register_fn: Option<ErasedRegisterFn>,
96 default_virtual_fn: Option<GodotGetVirtual>, user_virtual_fn: Option<GodotGetVirtual>, godot_params: GodotCreationInfo,
101
102 #[allow(dead_code)] init_level: InitLevel,
104 is_editor_plugin: bool,
105
106 dynify_fns_by_trait: HashMap<any::TypeId, DynTraitImpl>,
108
109 component_already_filled: [bool; 4],
111}
112
113impl ClassRegistrationInfo {
114 fn validate_unique(&mut self, item: &PluginItem) {
115 let index = match item {
119 PluginItem::Struct { .. } => 0,
120 PluginItem::InherentImpl(_) => 1,
121 PluginItem::ITraitImpl { .. } => 2,
122
123 PluginItem::DynTraitImpl { .. } => return,
126 };
127
128 if self.component_already_filled[index] {
129 panic!(
130 "Godot class `{}` is defined multiple times in Rust; you can rename it with #[class(rename=NewName)]",
131 self.class_name,
132 )
133 }
134
135 self.component_already_filled[index] = true;
136 }
137}
138
139#[expect(dead_code)] pub(crate) fn register_class<
142 T: cap::GodotDefault
143 + cap::ImplementsGodotVirtual
144 + cap::GodotToString
145 + cap::GodotNotification
146 + cap::GodotRegisterClass
147 + GodotClass,
148>() {
149 out!("Manually register class {}", std::any::type_name::<T>());
152
153 let godot_params = GodotCreationInfo {
154 to_string_func: Some(callbacks::to_string::<T>),
155 notification_func: Some(callbacks::on_notification::<T>),
156 reference_func: Some(callbacks::reference::<T>),
157 unreference_func: Some(callbacks::unreference::<T>),
158 create_instance_func: Some(callbacks::create::<T>),
159 free_instance_func: Some(callbacks::free::<T>),
160 get_virtual_func: Some(callbacks::get_virtual::<T>),
161 class_userdata: ptr::null_mut(), ..default_creation_info()
163 };
164
165 assert!(
166 !T::class_name().is_none(),
167 "cannot register () or unnamed class"
168 );
169
170 register_class_raw(ClassRegistrationInfo {
171 class_name: T::class_name(),
172 parent_class_name: Some(T::Base::class_name()),
173 register_methods_constants_fn: None,
174 register_properties_fn: None,
175 user_register_fn: Some(ErasedRegisterFn {
176 raw: callbacks::register_class_by_builder::<T>,
177 }),
178 user_virtual_fn: None,
179 default_virtual_fn: None,
180 godot_params,
181 init_level: T::INIT_LEVEL,
182 is_editor_plugin: false,
183 dynify_fns_by_trait: HashMap::new(),
184 component_already_filled: Default::default(), });
186}
187
188pub fn auto_register_classes(init_level: InitLevel) {
190 out!("Auto-register classes at level `{init_level:?}`...");
191
192 let mut map = HashMap::<ClassName, ClassRegistrationInfo>::new();
197
198 crate::private::iterate_plugins(|elem: &ClassPlugin| {
199 if elem.init_level != init_level {
201 return;
202 }
203
204 let name = elem.class_name;
207 let class_info = map
208 .entry(name)
209 .or_insert_with(|| default_registration_info(name));
210
211 fill_class_info(elem.item.clone(), class_info);
212 });
213
214 register_classes_and_dyn_traits(&mut map, init_level);
219
220 let mut editor_plugins: Vec<ClassName> = Vec::new();
223
224 for info in map.into_values() {
226 #[cfg(feature = "debug-log")] #[cfg_attr(published_docs, doc(cfg(feature = "debug-log")))]
227 let class_name = info.class_name;
228
229 if info.is_editor_plugin {
230 editor_plugins.push(info.class_name);
231 }
232
233 register_class_raw(info);
234
235 out!("Class {class_name} loaded.");
236 }
237
238 for editor_plugin_class_name in editor_plugins {
243 unsafe { interface_fn!(editor_add_plugin)(editor_plugin_class_name.string_sys()) };
244 }
245
246 out!("All classes for level `{init_level:?}` auto-registered.");
247}
248
249fn register_classes_and_dyn_traits(
250 map: &mut HashMap<ClassName, ClassRegistrationInfo>,
251 init_level: InitLevel,
252) {
253 let mut loaded_classes_by_level = global_loaded_classes_by_init_level();
254 let mut loaded_classes_by_name = global_loaded_classes_by_name();
255 let mut dyn_traits_by_typeid = global_dyn_traits_by_typeid();
256
257 for info in map.values_mut() {
258 let class_name = info.class_name;
259 out!("Register class: {class_name} at level `{init_level:?}`");
260
261 let loaded_class = LoadedClass {
262 name: class_name,
263 is_editor_plugin: info.is_editor_plugin,
264 };
265 let metadata = ClassMetadata {};
266
267 for (trait_type_id, mut dyn_trait_impl) in info.dynify_fns_by_trait.drain() {
269 dyn_trait_impl.parent_class_name = info.parent_class_name;
271
272 dyn_traits_by_typeid
273 .entry(trait_type_id)
274 .or_default()
275 .push(dyn_trait_impl);
276 }
277
278 loaded_classes_by_level
279 .entry(init_level)
280 .or_default()
281 .push(loaded_class);
282
283 loaded_classes_by_name.insert(class_name, metadata);
284 }
285}
286
287pub fn unregister_classes(init_level: InitLevel) {
288 let mut loaded_classes_by_level = global_loaded_classes_by_init_level();
289 let mut loaded_classes_by_name = global_loaded_classes_by_name();
290 let loaded_classes_current_level = loaded_classes_by_level
293 .remove(&init_level)
294 .unwrap_or_default();
295
296 out!("Unregister classes of level {init_level:?}...");
297 for class in loaded_classes_current_level.into_iter().rev() {
298 loaded_classes_by_name.remove(&class.name);
300
301 unregister_class_raw(class);
303 }
304}
305
306#[cfg(feature = "codegen-full")] #[cfg_attr(published_docs, doc(cfg(feature = "codegen-full")))]
307pub fn auto_register_rpcs<T: GodotClass>(object: &mut T) {
308 if let Some(InherentImpl {
310 register_rpcs_fn: Some(closure),
311 ..
312 }) = crate::private::find_inherent_impl(T::class_name())
313 {
314 (closure.raw)(object);
315 }
316}
317
318pub(crate) fn try_dynify_object<T: GodotClass, D: ?Sized + 'static>(
327 mut object: Gd<T>,
328) -> Result<DynGd<T, D>, (FromGodotError, Gd<T>)> {
329 let typeid = any::TypeId::of::<D>();
330 let trait_name = sys::short_type_name::<D>();
331
332 let dyn_traits_by_typeid = global_dyn_traits_by_typeid();
334 let Some(relations) = dyn_traits_by_typeid.get(&typeid) else {
335 return Err((FromGodotError::UnregisteredDynTrait { trait_name }, object));
336 };
337
338 for relation in relations {
341 match relation.get_dyn_gd(object) {
342 Ok(dyn_gd) => return Ok(dyn_gd),
343 Err(obj) => object = obj,
344 }
345 }
346
347 let error = FromGodotError::UnimplementedDynTrait {
348 trait_name,
349 class_name: object.dynamic_class_string().to_string(),
350 };
351
352 Err((error, object))
353}
354
355pub(crate) fn get_dyn_property_hint_string<T, D>() -> String
363where
364 T: GodotClass,
365 D: ?Sized + 'static,
366{
367 if T::inherits::<classes::Node>() {
369 return T::class_name().to_string();
370 }
371
372 let typeid = any::TypeId::of::<D>();
373 let dyn_traits_by_typeid = global_dyn_traits_by_typeid();
374
375 let Some(relations) = dyn_traits_by_typeid.get(&typeid) else {
376 let trait_name = sys::short_type_name::<D>();
377 godot_warn!(
378 "godot-rust: No class has been linked to trait {trait_name} with #[godot_dyn]."
379 );
380 return String::new();
381 };
382 assert!(
383 !relations.is_empty(),
384 "Trait {trait_name} has been registered as DynGd Trait \
385 despite no class being related to it \n\
386 **this is a bug, please report it**",
387 trait_name = sys::short_type_name::<D>()
388 );
389
390 let relations_iter = relations.iter().filter_map(|implementor| {
393 if implementor.parent_class_name? == T::class_name()
395 || ClassDb::singleton().is_parent_class(
396 &implementor.parent_class_name?.to_string_name(),
397 &T::class_name().to_string_name(),
398 )
399 {
400 Some(implementor)
401 } else {
402 None
403 }
404 });
405
406 join_with(relations_iter, ", ", |dyn_trait| {
407 dyn_trait.class_name().to_cow_str()
408 })
409}
410
411fn fill_class_info(item: PluginItem, c: &mut ClassRegistrationInfo) {
413 c.validate_unique(&item);
414
415 match item {
418 PluginItem::Struct(Struct {
419 base_class_name,
420 generated_create_fn,
421 generated_recreate_fn,
422 register_properties_fn,
423 free_fn,
424 default_get_virtual_fn,
425 is_tool,
426 is_editor_plugin,
427 is_internal,
428 is_instantiable,
429 #[cfg(all(since_api = "4.3", feature = "register-docs"))] #[cfg_attr(published_docs, doc(cfg(all(since_api = "4.3", feature = "register-docs"))))]
430 docs: _,
431 reference_fn,
432 unreference_fn,
433 }) => {
434 c.parent_class_name = Some(base_class_name);
435 c.default_virtual_fn = default_get_virtual_fn;
436 c.register_properties_fn = Some(register_properties_fn);
437 c.is_editor_plugin = is_editor_plugin;
438
439 c.godot_params.is_abstract = sys::conv::bool_to_sys(!is_instantiable);
448 c.godot_params.free_instance_func = Some(free_fn);
449 c.godot_params.reference_func = reference_fn;
450 c.godot_params.unreference_func = unreference_fn;
451
452 fill_into(
453 &mut c.godot_params.create_instance_func,
454 generated_create_fn,
455 )
456 .expect("duplicate: create_instance_func (def)");
457
458 #[cfg(before_api = "4.2")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.2")))]
459 let _ = is_internal; #[cfg(since_api = "4.2")]
461 {
462 fill_into(
463 &mut c.godot_params.recreate_instance_func,
464 generated_recreate_fn,
465 )
466 .expect("duplicate: recreate_instance_func (def)");
467
468 c.godot_params.is_exposed = sys::conv::bool_to_sys(!is_internal);
469 }
470
471 #[cfg(before_api = "4.2")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.2")))]
472 assert!(generated_recreate_fn.is_none()); #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
475 let _ = is_tool; #[cfg(since_api = "4.3")]
477 {
478 c.godot_params.is_runtime =
479 sys::conv::bool_to_sys(crate::private::is_class_runtime(is_tool));
480 }
481 }
482
483 PluginItem::InherentImpl(InherentImpl {
484 register_methods_constants_fn,
485 register_rpcs_fn: _,
486 #[cfg(all(since_api = "4.3", feature = "register-docs"))] #[cfg_attr(published_docs, doc(cfg(all(since_api = "4.3", feature = "register-docs"))))]
487 docs: _,
488 }) => {
489 c.register_methods_constants_fn = Some(register_methods_constants_fn);
490 }
491
492 PluginItem::ITraitImpl(ITraitImpl {
493 user_register_fn,
494 user_create_fn,
495 user_recreate_fn,
496 user_to_string_fn,
497 user_on_notification_fn,
498 user_set_fn,
499 user_get_fn,
500 get_virtual_fn,
501 user_get_property_list_fn,
502 user_free_property_list_fn,
503 user_property_can_revert_fn,
504 user_property_get_revert_fn,
505 #[cfg(all(since_api = "4.3", feature = "register-docs"))] #[cfg_attr(published_docs, doc(cfg(all(since_api = "4.3", feature = "register-docs"))))]
506 virtual_method_docs: _,
507 #[cfg(since_api = "4.2")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.2")))]
508 validate_property_fn,
509 }) => {
510 c.user_register_fn = user_register_fn;
511
512 fill_into(&mut c.godot_params.create_instance_func, user_create_fn)
516 .expect("duplicate: create_instance_func (i)");
517
518 #[cfg(since_api = "4.2")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.2")))]
519 fill_into(&mut c.godot_params.recreate_instance_func, user_recreate_fn)
520 .expect("duplicate: recreate_instance_func (i)");
521
522 #[cfg(before_api = "4.2")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.2")))]
523 assert!(user_recreate_fn.is_none()); c.godot_params.to_string_func = user_to_string_fn;
526 c.godot_params.notification_func = user_on_notification_fn;
527 c.godot_params.set_func = user_set_fn;
528 c.godot_params.get_func = user_get_fn;
529 c.godot_params.get_property_list_func = user_get_property_list_fn;
530 c.godot_params.free_property_list_func = user_free_property_list_fn;
531 c.godot_params.property_can_revert_func = user_property_can_revert_fn;
532 c.godot_params.property_get_revert_func = user_property_get_revert_fn;
533 c.user_virtual_fn = get_virtual_fn;
534 #[cfg(since_api = "4.2")]
535 {
536 c.godot_params.validate_property_func = validate_property_fn;
537 }
538 }
539 PluginItem::DynTraitImpl(dyn_trait_impl) => {
540 let type_id = dyn_trait_impl.dyn_trait_typeid();
541
542 let prev = c.dynify_fns_by_trait.insert(type_id, dyn_trait_impl);
543
544 assert!(
545 prev.is_none(),
546 "Duplicate registration of {:?} for class {}",
547 type_id,
548 c.class_name
549 );
550 }
551 }
552 }
555
556fn fill_into<T>(dst: &mut Option<T>, src: Option<T>) -> Result<(), ()> {
558 match (dst, src) {
559 (dst @ None, src) => *dst = src,
560 (Some(_), Some(_)) => return Err(()),
561 (Some(_), None) => { }
562 }
563 Ok(())
564}
565
566fn register_class_raw(mut info: ClassRegistrationInfo) {
568 validate_class_constraints(&info);
572
573 let class_name = info.class_name;
574 let parent_class_name = info
575 .parent_class_name
576 .expect("class defined (parent_class_name)");
577
578 if info.godot_params.get_virtual_func.is_none() {
581 info.godot_params.get_virtual_func = info.user_virtual_fn.or(info.default_virtual_fn);
582 }
583
584 let registration_failed = unsafe {
586 #[cfg(before_api = "4.2")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.2")))]
589 let register_fn = interface_fn!(classdb_register_extension_class);
590
591 #[cfg(all(since_api = "4.2", before_api = "4.3"))] #[cfg_attr(published_docs, doc(cfg(all(since_api = "4.2", before_api = "4.3"))))]
592 let register_fn = interface_fn!(classdb_register_extension_class2);
593
594 #[cfg(all(since_api = "4.3", before_api = "4.4"))] #[cfg_attr(published_docs, doc(cfg(all(since_api = "4.3", before_api = "4.4"))))]
595 let register_fn = interface_fn!(classdb_register_extension_class3);
596
597 #[cfg(since_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.4")))]
598 let register_fn = interface_fn!(classdb_register_extension_class4);
599
600 let _: () = register_fn(
601 sys::get_library(),
602 class_name.string_sys(),
603 parent_class_name.string_sys(),
604 ptr::addr_of!(info.godot_params),
605 );
606
607 let tag = interface_fn!(classdb_get_class_tag)(class_name.string_sys());
610 tag.is_null()
611 };
612
613 if registration_failed {
616 godot_error!(
617 "Failed to register class `{class_name}`; check preceding Godot stderr messages."
618 );
619 }
620
621 let mut class_builder = 0; if let Some(register_fn) = info.register_methods_constants_fn {
631 (register_fn.raw)(&mut class_builder);
632 }
633
634 if let Some(register_fn) = info.register_properties_fn {
635 (register_fn.raw)(&mut class_builder);
636 }
637
638 if let Some(register_fn) = info.user_register_fn {
639 (register_fn.raw)(&mut class_builder);
640 }
641}
642
643fn validate_class_constraints(_class: &ClassRegistrationInfo) {
644 }
646
647fn unregister_class_raw(class: LoadedClass) {
648 let class_name = class.name;
649 out!("Unregister class: {class_name}");
650
651 if class.is_editor_plugin {
653 unsafe {
654 interface_fn!(editor_remove_plugin)(class_name.string_sys());
655 }
656
657 out!("> Editor plugin removed");
658 }
659
660 #[allow(clippy::let_unit_value)]
661 let _: () = unsafe {
662 interface_fn!(classdb_unregister_extension_class)(
663 sys::get_library(),
664 class_name.string_sys(),
665 )
666 };
667
668 out!("Class {class_name} unloaded");
669}
670
671fn lock_or_panic<T>(global: &'static Global<T>, ctx: &str) -> GlobalGuard<'static, T> {
672 match global.try_lock() {
673 Ok(it) => it,
674 Err(err) => match err {
675 GlobalLockError::Poisoned { .. } => panic!(
676 "global lock for {ctx} poisoned; class registration or deregistration may have panicked"
677 ),
678 GlobalLockError::WouldBlock => panic!("unexpected concurrent access to global lock for {ctx}"),
679 GlobalLockError::InitFailed => unreachable!("global lock for {ctx} not initialized"),
680 },
681 }
682}
683
684fn default_registration_info(class_name: ClassName) -> ClassRegistrationInfo {
690 ClassRegistrationInfo {
691 class_name,
692 parent_class_name: None,
693 register_methods_constants_fn: None,
694 register_properties_fn: None,
695 user_register_fn: None,
696 default_virtual_fn: None,
697 user_virtual_fn: None,
698 godot_params: default_creation_info(),
699 init_level: InitLevel::Scene,
700 is_editor_plugin: false,
701 dynify_fns_by_trait: HashMap::new(),
702 component_already_filled: Default::default(), }
704}
705
706#[cfg(before_api = "4.2")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.2")))]
707fn default_creation_info() -> sys::GDExtensionClassCreationInfo {
708 sys::GDExtensionClassCreationInfo {
709 is_virtual: false as u8,
710 is_abstract: false as u8,
711 set_func: None,
712 get_func: None,
713 get_property_list_func: None,
714 free_property_list_func: None,
715 property_can_revert_func: None,
716 property_get_revert_func: None,
717 notification_func: None,
718 to_string_func: None,
719 reference_func: None,
720 unreference_func: None,
721 create_instance_func: None,
722 free_instance_func: None,
723 get_virtual_func: None,
724 get_rid_func: None,
725 class_userdata: ptr::null_mut(),
726 }
727}
728
729#[cfg(all(since_api = "4.2", before_api = "4.3"))] #[cfg_attr(published_docs, doc(cfg(all(since_api = "4.2", before_api = "4.3"))))]
730fn default_creation_info() -> sys::GDExtensionClassCreationInfo2 {
731 sys::GDExtensionClassCreationInfo2 {
732 is_virtual: false as u8,
733 is_abstract: false as u8,
734 is_exposed: sys::conv::SYS_TRUE,
735 set_func: None,
736 get_func: None,
737 get_property_list_func: None,
738 free_property_list_func: None,
739 property_can_revert_func: None,
740 property_get_revert_func: None,
741 validate_property_func: None,
742 notification_func: None,
743 to_string_func: None,
744 reference_func: None,
745 unreference_func: None,
746 create_instance_func: None,
747 free_instance_func: None,
748 recreate_instance_func: None,
749 get_virtual_func: None,
750 get_virtual_call_data_func: None,
751 call_virtual_with_data_func: None,
752 get_rid_func: None,
753 class_userdata: ptr::null_mut(),
754 }
755}
756
757#[cfg(all(since_api = "4.3", before_api = "4.4"))] #[cfg_attr(published_docs, doc(cfg(all(since_api = "4.3", before_api = "4.4"))))]
758fn default_creation_info() -> sys::GDExtensionClassCreationInfo3 {
759 sys::GDExtensionClassCreationInfo3 {
760 is_virtual: false as u8,
761 is_abstract: false as u8,
762 is_exposed: sys::conv::SYS_TRUE,
763 is_runtime: sys::conv::SYS_TRUE,
764 set_func: None,
765 get_func: None,
766 get_property_list_func: None,
767 free_property_list_func: None,
768 property_can_revert_func: None,
769 property_get_revert_func: None,
770 validate_property_func: None,
771 notification_func: None,
772 to_string_func: None,
773 reference_func: None,
774 unreference_func: None,
775 create_instance_func: None,
776 free_instance_func: None,
777 recreate_instance_func: None,
778 get_virtual_func: None,
779 get_virtual_call_data_func: None,
780 call_virtual_with_data_func: None,
781 get_rid_func: None,
782 class_userdata: ptr::null_mut(),
783 }
784}
785
786#[cfg(since_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.4")))]
787fn default_creation_info() -> sys::GDExtensionClassCreationInfo4 {
788 sys::GDExtensionClassCreationInfo4 {
789 is_virtual: false as u8,
790 is_abstract: false as u8,
791 is_exposed: sys::conv::SYS_TRUE,
792 is_runtime: sys::conv::SYS_TRUE,
793 icon_path: ptr::null(),
794 set_func: None,
795 get_func: None,
796 get_property_list_func: None,
797 free_property_list_func: None,
798 property_can_revert_func: None,
799 property_get_revert_func: None,
800 validate_property_func: None,
801 notification_func: None,
802 to_string_func: None,
803 reference_func: None,
804 unreference_func: None,
805 create_instance_func: None,
806 free_instance_func: None,
807 recreate_instance_func: None,
808 get_virtual_func: None,
809 get_virtual_call_data_func: None,
810 call_virtual_with_data_func: None,
811 class_userdata: ptr::null_mut(),
812 }
813}