1#![allow(clippy::missing_safety_doc)]
12
13use std::any::Any;
14
15use godot_ffi as sys;
16use sys::conv::u32_to_usize;
17use sys::interface_fn;
18
19use crate::builder::ClassBuilder;
20use crate::builtin::{StringName, Variant};
21use crate::classes::Object;
22use crate::meta::PropertyInfo;
23use crate::obj::{bounds, cap, AsDyn, Base, Bounds, Gd, GodotClass, Inherits, UserClass};
24use crate::private::{handle_panic, IntoVirtualMethodReceiver, PanicPayload};
25use crate::registry::plugin::ErasedDynGd;
26use crate::storage::{as_storage, InstanceStorage, Storage, StorageRefCounted};
27
28#[cfg(since_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.4")))]
34pub unsafe extern "C" fn create<T: cap::GodotDefault>(
35 _class_userdata: *mut std::ffi::c_void,
36 _notify_postinitialize: sys::GDExtensionBool,
37) -> sys::GDExtensionObjectPtr {
38 create_custom(
39 T::__godot_user_init,
40 sys::conv::bool_from_sys(_notify_postinitialize),
41 )
42 .unwrap_or(std::ptr::null_mut())
43}
44
45#[cfg(before_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.4")))]
46pub unsafe extern "C" fn create<T: cap::GodotDefault>(
47 _class_userdata: *mut std::ffi::c_void,
48) -> sys::GDExtensionObjectPtr {
49 create_custom(T::__godot_user_init, true).unwrap_or(std::ptr::null_mut())
51}
52
53#[cfg(all(since_api = "4.4", before_api = "4.5"))] #[cfg_attr(published_docs, doc(cfg(all(since_api = "4.4", before_api = "4.5"))))]
57pub unsafe extern "C" fn create_null<T>(
58 _class_userdata: *mut std::ffi::c_void,
59 _notify_postinitialize: sys::GDExtensionBool,
60) -> sys::GDExtensionObjectPtr {
61 std::ptr::null_mut()
62}
63
64#[cfg(before_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.4")))]
65pub unsafe extern "C" fn create_null<T>(
66 _class_userdata: *mut std::ffi::c_void,
67) -> sys::GDExtensionObjectPtr {
68 std::ptr::null_mut()
69}
70
71pub unsafe extern "C" fn recreate<T: cap::GodotDefault>(
75 _class_userdata: *mut std::ffi::c_void,
76 object: sys::GDExtensionObjectPtr,
77) -> sys::GDExtensionClassInstancePtr {
78 create_rust_part_for_existing_godot_part(T::__godot_user_init, object, |_| {})
79 .unwrap_or(std::ptr::null_mut())
80}
81
82#[cfg(before_api = "4.5")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.5")))]
86pub unsafe extern "C" fn recreate_null<T>(
87 _class_userdata: *mut std::ffi::c_void,
88 _object: sys::GDExtensionObjectPtr,
89) -> sys::GDExtensionClassInstancePtr {
90 std::ptr::null_mut()
91}
92
93pub(crate) fn create_custom<T, F>(
94 make_user_instance: F,
95 notify_postinitialize: bool,
96) -> Result<sys::GDExtensionObjectPtr, PanicPayload>
97where
98 T: GodotClass,
99 F: FnOnce(Base<T::Base>) -> T,
100{
101 let base_class_name = T::Base::class_id();
102 let base_ptr = unsafe { sys::classdb_construct_object(base_class_name.string_sys()) };
103
104 let postinit = |base_ptr| {
105 #[cfg(since_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.4")))]
106 if notify_postinitialize {
107 let mut obj = unsafe { Gd::<Object>::from_obj_sys_weak(base_ptr) };
109 obj.notify(crate::classes::notify::ObjectNotification::POSTINITIALIZE);
110 obj.drop_weak();
111 }
112 };
113
114 match create_rust_part_for_existing_godot_part(make_user_instance, base_ptr, postinit) {
115 Ok(_extension_ptr) => Ok(base_ptr),
116 Err(payload) => {
117 unsafe { interface_fn!(object_destroy)(base_ptr) };
120
121 Err(payload)
122 }
123 }
124
125 }
127
128fn create_rust_part_for_existing_godot_part<T, F, P>(
134 make_user_instance: F,
135 base_ptr: sys::GDExtensionObjectPtr,
136 postinit: P,
137) -> Result<sys::GDExtensionClassInstancePtr, PanicPayload>
138where
139 T: GodotClass,
140 F: FnOnce(Base<T::Base>) -> T,
141 P: Fn(sys::GDExtensionObjectPtr),
142{
143 let class_name = T::class_id();
144 let base = unsafe { Base::from_sys(base_ptr) };
147
148 let context = || format!("panic during {class_name}::init() constructor");
150 let code = || make_user_instance(unsafe { Base::from_base(&base) });
151 let user_instance = handle_panic(context, std::panic::AssertUnwindSafe(code))?;
152
153 let mut base_copy = unsafe { Base::from_base(&base) };
157
158 let instance = InstanceStorage::<T>::construct(user_instance, base);
159 let instance_rust_ptr = instance.into_raw();
160 let instance_ptr = instance_rust_ptr as sys::GDExtensionClassInstancePtr;
161
162 let binding_data_callbacks = crate::storage::nop_instance_callbacks();
163 unsafe {
164 interface_fn!(object_set_instance)(base_ptr, class_name.string_sys(), instance_ptr);
165 interface_fn!(object_set_instance_binding)(
166 base_ptr,
167 sys::get_library() as *mut std::ffi::c_void,
168 instance_ptr as *mut std::ffi::c_void,
169 &binding_data_callbacks,
170 );
171 }
172
173 postinit(base_ptr);
174
175 base_copy.mark_initialized();
177
178 Ok(instance_ptr)
180}
181
182pub unsafe extern "C" fn free<T: GodotClass>(
183 _class_user_data: *mut std::ffi::c_void,
184 instance: sys::GDExtensionClassInstancePtr,
185) {
186 {
187 let storage = as_storage::<T>(instance);
188 storage.mark_destroyed_by_godot();
189 } crate::storage::destroy_storage::<T>(instance);
192}
193
194#[cfg(since_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.4")))]
195pub unsafe extern "C" fn get_virtual<T: cap::ImplementsGodotVirtual>(
196 _class_user_data: *mut std::ffi::c_void,
197 name: sys::GDExtensionConstStringNamePtr,
198 hash: u32,
199) -> sys::GDExtensionClassCallVirtual {
200 let borrowed_string = StringName::borrow_string_sys(name);
202 let method_name = borrowed_string.to_string();
203
204 T::__virtual_call(method_name.as_str(), hash)
205}
206
207#[cfg(before_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.4")))]
208pub unsafe extern "C" fn get_virtual<T: cap::ImplementsGodotVirtual>(
209 _class_user_data: *mut std::ffi::c_void,
210 name: sys::GDExtensionConstStringNamePtr,
211) -> sys::GDExtensionClassCallVirtual {
212 let borrowed_string = StringName::borrow_string_sys(name);
214 let method_name = borrowed_string.to_string();
215
216 T::__virtual_call(method_name.as_str())
217}
218
219#[cfg(since_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.4")))]
220pub unsafe extern "C" fn default_get_virtual<T: UserClass>(
221 _class_user_data: *mut std::ffi::c_void,
222 name: sys::GDExtensionConstStringNamePtr,
223 hash: u32,
224) -> sys::GDExtensionClassCallVirtual {
225 let borrowed_string = StringName::borrow_string_sys(name);
227 let method_name = borrowed_string.to_string();
228
229 T::__default_virtual_call(method_name.as_str(), hash)
230}
231
232#[cfg(before_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.4")))]
233pub unsafe extern "C" fn default_get_virtual<T: UserClass>(
234 _class_user_data: *mut std::ffi::c_void,
235 name: sys::GDExtensionConstStringNamePtr,
236) -> sys::GDExtensionClassCallVirtual {
237 let borrowed_string = StringName::borrow_string_sys(name);
239 let method_name = borrowed_string.to_string();
240
241 T::__default_virtual_call(method_name.as_str())
242}
243
244pub unsafe extern "C" fn to_string<T: cap::GodotToString>(
245 instance: sys::GDExtensionClassInstancePtr,
246 is_valid: *mut sys::GDExtensionBool,
247 out_string: sys::GDExtensionStringPtr,
248) {
249 let storage = as_storage::<T>(instance);
252 let string = T::__godot_to_string(T::Recv::instance(storage));
253
254 string.move_into_string_ptr(out_string);
256
257 *is_valid = sys::conv::SYS_TRUE;
259}
260
261pub unsafe extern "C" fn on_notification<T: cap::GodotNotification>(
262 instance: sys::GDExtensionClassInstancePtr,
263 what: i32,
264 _reversed: sys::GDExtensionBool,
265) {
266 let storage = as_storage::<T>(instance);
267 let mut instance = storage.get_mut();
268
269 T::__godot_notification(&mut *instance, what);
270}
271
272pub unsafe extern "C" fn get_property<T: cap::GodotGet>(
273 instance: sys::GDExtensionClassInstancePtr,
274 name: sys::GDExtensionConstStringNamePtr,
275 ret: sys::GDExtensionVariantPtr,
276) -> sys::GDExtensionBool {
277 let storage = as_storage::<T>(instance);
278 let instance = T::Recv::instance(storage);
279 let property = StringName::new_from_string_sys(name);
280
281 match T::__godot_get_property(instance, property) {
282 Some(value) => {
283 value.move_into_var_ptr(ret);
284 sys::conv::SYS_TRUE
285 }
286 None => sys::conv::SYS_FALSE,
287 }
288}
289
290pub unsafe extern "C" fn set_property<T: cap::GodotSet>(
291 instance: sys::GDExtensionClassInstancePtr,
292 name: sys::GDExtensionConstStringNamePtr,
293 value: sys::GDExtensionConstVariantPtr,
294) -> sys::GDExtensionBool {
295 let storage = as_storage::<T>(instance);
296 let instance = T::Recv::instance(storage);
297
298 let property = StringName::new_from_string_sys(name);
299 let value = Variant::new_from_var_sys(value);
300
301 sys::conv::bool_to_sys(T::__godot_set_property(instance, property, value))
302}
303
304pub unsafe extern "C" fn reference<T: GodotClass>(instance: sys::GDExtensionClassInstancePtr) {
305 let storage = as_storage::<T>(instance);
306 storage.on_inc_ref();
307}
308
309pub unsafe extern "C" fn unreference<T: GodotClass>(instance: sys::GDExtensionClassInstancePtr) {
310 let storage = as_storage::<T>(instance);
311 storage.on_dec_ref();
312}
313
314#[deny(unsafe_op_in_unsafe_fn)]
318pub unsafe extern "C" fn get_property_list<T: cap::GodotGetPropertyList>(
319 instance: sys::GDExtensionClassInstancePtr,
320 count: *mut u32,
321) -> *const sys::GDExtensionPropertyInfo {
322 let storage = unsafe { as_storage::<T>(instance) };
324 let instance = T::Recv::instance(storage);
325
326 let property_list = T::__godot_get_property_list(instance);
327 let property_list_sys: Box<[sys::GDExtensionPropertyInfo]> = property_list
328 .into_iter()
329 .map(|prop| prop.into_owned_property_sys())
330 .collect();
331
332 unsafe {
334 *count = property_list_sys
335 .len()
336 .try_into()
337 .expect("property list cannot be longer than `u32::MAX`");
338 }
339
340 Box::leak(property_list_sys).as_mut_ptr()
341}
342
343#[deny(unsafe_op_in_unsafe_fn)]
348pub unsafe extern "C" fn free_property_list<T: cap::GodotGetPropertyList>(
349 _instance: sys::GDExtensionClassInstancePtr,
350 list: *const sys::GDExtensionPropertyInfo,
351 count: u32,
352) {
353 let list = list as *mut sys::GDExtensionPropertyInfo;
354
355 let property_list_slice = unsafe { std::slice::from_raw_parts_mut(list, u32_to_usize(count)) };
361
362 let property_list_sys = unsafe { Box::from_raw(property_list_slice) };
366
367 for property_info in property_list_sys.iter() {
368 unsafe {
371 crate::meta::PropertyInfo::free_owned_property_sys(*property_info);
372 }
373 }
374}
375
376#[deny(unsafe_op_in_unsafe_fn)]
381unsafe fn raw_property_get_revert<T: cap::GodotPropertyGetRevert>(
382 instance: sys::GDExtensionClassInstancePtr,
383 property_name: sys::GDExtensionConstStringNamePtr,
384) -> Option<Variant> {
385 let storage = unsafe { as_storage::<T>(instance) };
387 let instance = T::Recv::instance(storage);
388
389 let property = unsafe { StringName::borrow_string_sys(property_name) };
391 T::__godot_property_get_revert(instance, property.clone())
392}
393
394#[deny(unsafe_op_in_unsafe_fn)]
398pub unsafe extern "C" fn property_can_revert<T: cap::GodotPropertyGetRevert>(
399 instance: sys::GDExtensionClassInstancePtr,
400 property_name: sys::GDExtensionConstStringNamePtr,
401) -> sys::GDExtensionBool {
402 let revert = unsafe { raw_property_get_revert::<T>(instance, property_name) };
404
405 sys::conv::bool_to_sys(revert.is_some())
406}
407
408#[deny(unsafe_op_in_unsafe_fn)]
412pub unsafe extern "C" fn property_get_revert<T: cap::GodotPropertyGetRevert>(
413 instance: sys::GDExtensionClassInstancePtr,
414 property_name: sys::GDExtensionConstStringNamePtr,
415 ret: sys::GDExtensionVariantPtr,
416) -> sys::GDExtensionBool {
417 let Some(revert) = (unsafe { raw_property_get_revert::<T>(instance, property_name) }) else {
419 return sys::conv::SYS_FALSE;
420 };
421
422 unsafe {
424 revert.move_into_var_ptr(ret);
425 }
426
427 sys::conv::SYS_TRUE
428}
429
430#[deny(unsafe_op_in_unsafe_fn)]
440pub unsafe extern "C" fn validate_property<T: cap::GodotValidateProperty>(
441 instance: sys::GDExtensionClassInstancePtr,
442 property_info_ptr: *mut sys::GDExtensionPropertyInfo,
443) -> sys::GDExtensionBool {
444 let storage = unsafe { as_storage::<T>(instance) };
446 let instance = T::Recv::instance(storage);
447
448 let mut property_info = unsafe { PropertyInfo::new_from_sys(property_info_ptr) };
450 T::__godot_validate_property(instance, &mut property_info);
451
452 unsafe { property_info.move_into_property_info_ptr(property_info_ptr) };
454
455 sys::conv::SYS_TRUE
456}
457
458pub fn register_class_by_builder<T: cap::GodotRegisterClass>(_class_builder: &mut dyn Any) {
462 let mut class_builder = ClassBuilder::new();
468 T::__godot_register_class(&mut class_builder);
469}
470
471pub fn register_user_properties<T: cap::ImplementsGodotExports>(_class_builder: &mut dyn Any) {
472 T::__register_exports();
473}
474
475pub fn register_user_methods_constants<T: cap::ImplementsGodotApi>(_class_builder: &mut dyn Any) {
476 T::__register_methods();
482 T::__register_constants();
483}
484
485pub fn register_user_rpcs<T: cap::ImplementsGodotApi>(object: &mut dyn Any) {
486 T::__register_rpcs(object);
487}
488
489#[deny(unsafe_op_in_unsafe_fn)]
493pub unsafe fn dynify_fn<T, D>(obj: Gd<Object>) -> ErasedDynGd
494where
495 T: GodotClass + Inherits<Object> + AsDyn<D> + Bounds<Declarer = bounds::DeclUser>,
496 D: ?Sized + 'static,
497{
498 let obj = unsafe { obj.try_cast::<T>().unwrap_unchecked() };
500 let obj = obj.into_dyn::<D>();
501 let obj = obj.upcast::<Object>();
502
503 ErasedDynGd {
504 boxed: Box::new(obj),
505 }
506}