gdnative_core/object/
raw.rs

1use std::fmt::{self, Debug};
2use std::marker::PhantomData;
3use std::ptr::{self, NonNull};
4
5use crate::core_types::GodotString;
6use crate::object::memory::RefCounted;
7use crate::private::get_api;
8use crate::sys;
9
10use super::GodotObject;
11
12/// An opaque struct representing Godot objects. This should never be created on the stack.
13///
14/// This is an internal interface. Users are expected to use references to named generated types
15/// instead.
16#[repr(C)]
17pub struct RawObject<T> {
18    _opaque: [u8; 0],
19    _marker: PhantomData<(T, *const ())>,
20}
21
22impl<T: GodotObject> RawObject<T> {
23    /// Creates a typed reference from a pointer, without checking the type of the pointer.
24    ///
25    /// # Safety
26    ///
27    /// The `obj` pointer must be pointing to a valid Godot object of type `T` during the
28    /// entirety of `'a`.
29    #[inline]
30    pub unsafe fn from_sys_ref_unchecked<'a>(obj: NonNull<sys::godot_object>) -> &'a Self {
31        &*(obj.as_ptr() as *mut Self)
32    }
33
34    /// Creates a typed reference from a pointer if the pointer is pointing to an object of
35    /// the correct type. Returns `None` otherwise.
36    ///
37    /// # Safety
38    ///
39    /// The `obj` pointer must be pointing to a valid Godot object during the entirety of `'a`.
40    #[inline]
41    pub unsafe fn try_from_sys_ref<'a>(obj: NonNull<sys::godot_object>) -> Option<&'a Self> {
42        if ptr_is_class(obj.as_ptr(), T::class_name()) {
43            Some(Self::from_sys_ref_unchecked(obj))
44        } else {
45            None
46        }
47    }
48
49    /// Casts a reference to this opaque object to `*const sys::godot_object`.
50    #[inline]
51    pub fn sys(&self) -> NonNull<sys::godot_object> {
52        // SAFETY: references should never be null
53        unsafe { NonNull::new_unchecked(self as *const _ as *mut _) }
54    }
55
56    /// Checks whether the object is of a certain Godot class.
57    #[inline]
58    pub fn is_class<U: GodotObject>(&self) -> bool {
59        self.is_class_by_name(U::class_name())
60    }
61
62    /// Checks whether the object is of a certain Godot class by name.
63    #[inline]
64    pub fn is_class_by_name(&self, class_name: &str) -> bool {
65        unsafe { ptr_is_class(self.sys().as_ptr(), class_name) }
66    }
67
68    /// Returns the class name of this object dynamically using `Object::get_class`.
69    #[inline]
70    pub fn class_name(&self) -> String {
71        let api = crate::private::get_api();
72        let get_class_method = crate::private::ObjectMethodTable::get(api).get_class;
73        let mut argument_buffer = [ptr::null() as *const libc::c_void; 0];
74        let mut class_name = sys::godot_string::default();
75        let ret_ptr = &mut class_name as *mut sys::godot_string;
76
77        unsafe {
78            (api.godot_method_bind_ptrcall)(
79                get_class_method,
80                self.sys().as_ptr(),
81                argument_buffer.as_mut_ptr() as *mut _,
82                ret_ptr as *mut _,
83            );
84        }
85
86        let string = GodotString::from_sys(class_name);
87        string.to_string()
88    }
89
90    /// Attempt to cast a Godot object to a different class type.
91    #[inline]
92    pub fn cast<U>(&self) -> Option<&RawObject<U>>
93    where
94        U: GodotObject,
95    {
96        unsafe { RawObject::try_from_sys_ref(self.sys()) }
97    }
98
99    /// Attempt to cast a Godot object to a different class type without checking the type at
100    /// runtime.
101    ///
102    /// # Safety
103    ///
104    /// The types must be compatible.
105    #[inline]
106    pub unsafe fn cast_unchecked<U>(&self) -> &RawObject<U>
107    where
108        U: GodotObject,
109    {
110        RawObject::from_sys_ref_unchecked(self.sys())
111    }
112
113    /// Free the underlying object.
114    ///
115    /// # Safety
116    ///
117    /// Further operations must not be performed on the same reference.
118    #[inline]
119    pub unsafe fn free(&self) {
120        (get_api().godot_object_destroy)(self.sys().as_ptr());
121    }
122}
123
124impl<T: GodotObject<Memory = RefCounted>> RawObject<T> {
125    /// Increase the reference count of the object.
126    #[inline]
127    pub fn add_ref(&self) {
128        let api = crate::private::get_api();
129        let addref_method = crate::private::ReferenceMethodTable::get(api).reference;
130        let mut argument_buffer = [ptr::null() as *const libc::c_void; 0];
131        let mut ok = false;
132        let ok_ptr = &mut ok as *mut bool;
133
134        unsafe {
135            (api.godot_method_bind_ptrcall)(
136                addref_method,
137                self.sys().as_ptr(),
138                argument_buffer.as_mut_ptr() as *mut _,
139                ok_ptr as *mut _,
140            );
141        }
142
143        // If this assertion blows up it means there is a reference counting bug
144        // and we tried to increment the ref count of a dead object (who's ref
145        // count is equal to zero).
146        debug_assert!(ok);
147    }
148
149    /// Decrease the reference count of the object. Returns `true` if this is the last
150    /// reference.
151    ///
152    /// # Safety
153    ///
154    /// Further operations must not be performed on the same reference if this is the last
155    /// reference.
156    #[inline]
157    pub unsafe fn unref(&self) -> bool {
158        let api = crate::private::get_api();
159        let unref_method = crate::private::ReferenceMethodTable::get(api).unreference;
160
161        let mut argument_buffer = [ptr::null() as *const libc::c_void; 0];
162        let mut last_reference = false;
163        let ret_ptr = &mut last_reference as *mut bool;
164        (api.godot_method_bind_ptrcall)(
165            unref_method,
166            self.sys().as_ptr(),
167            argument_buffer.as_mut_ptr() as *mut _,
168            ret_ptr as *mut _,
169        );
170
171        last_reference
172    }
173
174    /// Decrease the reference count of the object. Frees the object and returns `true` if this
175    /// is the last reference.
176    ///
177    /// # Safety
178    ///
179    /// Further operations must not be performed on the same reference if this is the last
180    /// reference.
181    #[inline]
182    pub unsafe fn unref_and_free_if_last(&self) -> bool {
183        let last_reference = self.unref();
184
185        if last_reference {
186            self.free();
187        }
188
189        last_reference
190    }
191
192    /// Initialize the reference count of the object.
193    ///
194    /// # Safety
195    ///
196    /// This function assumes that no other references are held at the time.
197    #[inline]
198    pub unsafe fn init_ref_count(&self) {
199        let obj = self.sys().as_ptr();
200
201        let api = crate::private::get_api();
202        let init_method = crate::private::ReferenceMethodTable::get(api).init_ref;
203
204        let mut argument_buffer = [ptr::null() as *const libc::c_void; 0];
205        let mut ok = false;
206        let ret_ptr = &mut ok as *mut bool;
207        (api.godot_method_bind_ptrcall)(
208            init_method,
209            obj,
210            argument_buffer.as_mut_ptr() as *mut _,
211            ret_ptr as *mut _,
212        );
213
214        debug_assert!(ok);
215    }
216}
217
218impl<T: GodotObject> Debug for RawObject<T> {
219    #[inline]
220    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221        write!(f, "{}({:p})", T::class_name(), self.sys())
222    }
223}
224
225/// Checks whether the raw object pointer is of a certain Godot class.
226///
227/// # Safety
228///
229/// The `obj` pointer must be pointing to a valid Godot object.
230#[inline]
231unsafe fn ptr_is_class(obj: *mut sys::godot_object, class_name: &str) -> bool {
232    let api = crate::private::get_api();
233    let method_bind = crate::private::ObjectMethodTable::get(api).is_class;
234
235    let mut class_name = (api.godot_string_chars_to_utf8_with_len)(
236        class_name.as_ptr() as *const _,
237        class_name.len() as _,
238    );
239
240    let mut argument_buffer = [ptr::null() as *const libc::c_void; 1];
241    argument_buffer[0] = (&class_name) as *const _ as *const _;
242
243    let mut ret = false;
244    let ret_ptr = &mut ret as *mut _;
245    (api.godot_method_bind_ptrcall)(
246        method_bind,
247        obj,
248        argument_buffer.as_mut_ptr() as *mut _,
249        ret_ptr as *mut _,
250    );
251
252    (api.godot_string_destroy)(&mut class_name);
253
254    ret
255}