gdnative_core/object/
instance.rs

1use std::ptr::NonNull;
2
3use crate::core_types::{
4    FromVariant, FromVariantError, GodotString, OwnedToVariant, ToVariant, Variant,
5};
6use crate::export::user_data::{Map, MapMut, MapOwned, UserData};
7use crate::export::{class_registry, emplace, NativeClass};
8use crate::object::bounds::{
9    AssumeSafeLifetime, LifetimeConstraint, RefImplBound, SafeAsRaw, SafeDeref,
10};
11use crate::object::memory::{ManuallyManaged, RefCounted};
12use crate::object::ownership::{NonUniqueOwnership, Ownership, Shared, ThreadLocal, Unique};
13use crate::object::{GodotObject, Instanciable, QueueFree, RawObject, Ref, TRef};
14use crate::private::{get_api, ReferenceCountedClassPlaceholder};
15
16/// A persistent reference to a GodotObject with a rust NativeClass attached.
17///
18/// `Instance`s can be worked on directly using `map` and `map_mut` if the base object is
19/// reference-counted. Otherwise, use `assume_safe` to obtain a temporary `TInstance`.
20///
21/// See the type-level documentation on `Ref` for more information on typed thread accesses.
22#[derive(Debug)]
23pub struct Instance<T: NativeClass, Own: Ownership = Shared> {
24    owner: Ref<T::Base, Own>,
25    script: T::UserData,
26}
27
28/// A reference to a GodotObject with a rust NativeClass attached that is assumed safe during
29/// a certain lifetime.
30///
31/// See the type-level documentation on `Ref` for more information on typed thread accesses.
32#[derive(Debug)]
33pub struct TInstance<'a, T: NativeClass, Own: Ownership = Shared> {
34    owner: TRef<'a, T::Base, Own>,
35    script: T::UserData,
36}
37
38impl<T: NativeClass> Instance<T, Unique> {
39    /// Creates a `T::Base` with the script `T` attached. Both `T::Base` and `T` must have zero
40    /// argument constructors.
41    ///
42    /// If `T::Base` is manually-managed, then the resulting `Instance` must be passed to
43    /// the engine or manually freed with `Instance::free`. Otherwise, the base object will be
44    /// leaked.
45    ///
46    /// Must be called after the library is initialized.
47    #[inline]
48    #[allow(clippy::new_without_default)]
49    pub fn new() -> Self
50    where
51        T::Base: Instanciable,
52    {
53        Self::maybe_emplace(None)
54    }
55
56    /// Creates a `T::Base` with a given instance of the script `T` attached. `T::Base` must
57    /// have a zero-argument constructor.
58    ///
59    /// This may be used to create instances of scripts that do not have zero-argument
60    /// constructors:
61    ///
62    /// ```ignore
63    /// // This type does not have a zero-argument constructor. As a result, `Instance::new`
64    /// // will panic and `Foo.new` from GDScript will result in errors when the object is used.
65    /// #[derive(NativeScript)]
66    /// #[inherit(Reference)]
67    /// #[no_constructor]
68    /// struct MyIntVector(i64, i64);
69    ///
70    /// #[methods]
71    /// impl MyIntVector {
72    ///     // - snip -
73    /// }
74    ///
75    /// // With `Instance::emplace`, however, we can expose "constructors" from a factory
76    /// // auto-load for our script type.
77    /// #[derive(NativeScript)]
78    /// #[inherit(Node)]
79    /// #[user_data(Aether<Self>)]
80    /// struct MyIntVectorFactory;
81    ///
82    /// #[methods]
83    /// impl MyIntVectorFactory {
84    ///     #[export]
85    ///     fn make(&self, _owner: &Node, x: i64, y: i64) -> Instance<MyIntVector, Unique> {
86    ///         Instance::emplace(MyIntVector(x, y))
87    ///     }
88    /// }
89    /// ```
90    ///
91    /// If `T::Base` is manually-managed, then the resulting `Instance` must be passed to
92    /// the engine or manually freed with `Instance::free`. Otherwise, the base object will be
93    /// leaked.
94    ///
95    /// Must be called after the library is initialized.
96    #[inline]
97    pub fn emplace(script: T) -> Self
98    where
99        T::Base: Instanciable,
100    {
101        Self::maybe_emplace(Some(script))
102    }
103
104    fn maybe_emplace(script: Option<T>) -> Self
105    where
106        T::Base: Instanciable,
107    {
108        unsafe {
109            let gd_api = get_api();
110            let nativescript_methods = crate::private::NativeScriptMethodTable::get(gd_api);
111
112            assert_ne!(
113                std::ptr::null(),
114                nativescript_methods.set_class_name,
115                "NativeScript::set_class_name must be available"
116            );
117            assert_ne!(
118                std::ptr::null(),
119                nativescript_methods.set_library,
120                "NativeScript::set_library must be available"
121            );
122            assert_ne!(
123                std::ptr::null(),
124                nativescript_methods.new,
125                "NativeScript::new must be available"
126            );
127
128            // The API functions take NUL-terminated C strings. &CStr is not used for its runtime cost.
129            let class_name = b"NativeScript\0".as_ptr() as *const libc::c_char;
130            let ctor = (gd_api.godot_get_class_constructor)(class_name).unwrap();
131
132            let native_script =
133                NonNull::new(ctor()).expect("NativeScript constructor should not return null");
134            let native_script =
135                RawObject::<ReferenceCountedClassPlaceholder>::from_sys_ref_unchecked(
136                    native_script,
137                );
138            native_script.init_ref_count();
139
140            // `set_library` should be called before `set_class_name` to trigger class registration
141            // before trying to fetch the class name, in case the first NativeScript instance of this
142            // library is being constructed from Rust.
143            let mut args: [*const libc::c_void; 1] = [crate::private::get_gdnative_library_sys()];
144            (gd_api.godot_method_bind_ptrcall)(
145                nativescript_methods.set_library,
146                native_script.sys().as_ptr(),
147                args.as_mut_ptr(),
148                std::ptr::null_mut(),
149            );
150
151            let script_class_name = class_registry::class_name::<T>()
152                .map(GodotString::from)
153                .unwrap_or_else(|| {
154                    panic!(
155                        "`{type_name}` must be registered before it can be used; call `handle.add_class::<{type_name}>()` in your `nativescript_init` callback",
156                        type_name = std::any::type_name::<T>(),
157                    );
158                });
159
160            let mut args: [*const libc::c_void; 1] = [script_class_name.sys() as *const _];
161            (gd_api.godot_method_bind_ptrcall)(
162                nativescript_methods.set_class_name,
163                native_script.sys().as_ptr(),
164                args.as_mut_ptr(),
165                std::ptr::null_mut(),
166            );
167
168            if let Some(script) = script {
169                emplace::place(script);
170            }
171
172            let mut args: [*const sys::godot_variant; 0] = [];
173            let variant = (gd_api.godot_method_bind_call)(
174                nativescript_methods.new,
175                native_script.sys().as_ptr(),
176                args.as_mut_ptr(),
177                0,
178                std::ptr::null_mut(),
179            );
180
181            assert!(
182                emplace::take::<T>().is_none(),
183                "emplacement value should be taken by the constructor wrapper (this is a bug in the bindings)",
184            );
185
186            let variant = Variant::from_sys(variant);
187
188            let owner = variant
189                .to_object::<T::Base>()
190                .expect("the engine should return a base object of the correct type")
191                .assume_unique();
192
193            let script_ptr =
194                (gd_api.godot_nativescript_get_userdata)(owner.sys()) as *const libc::c_void;
195
196            assert_ne!(
197                std::ptr::null(),
198                script_ptr,
199                "script instance should not be null (did the constructor fail?)"
200            );
201
202            let script = T::UserData::clone_from_user_data_unchecked(script_ptr);
203
204            native_script.unref();
205
206            Instance { owner, script }
207        }
208    }
209}
210
211impl<T: NativeClass, Own: Ownership> Instance<T, Own> {
212    /// Returns the base object, dropping the script wrapper.
213    #[inline]
214    pub fn into_base(self) -> Ref<T::Base, Own> {
215        self.owner
216    }
217
218    /// Returns the script wrapper.
219    #[inline]
220    pub fn into_script(self) -> T::UserData {
221        self.script
222    }
223
224    /// Returns the base object and the script wrapper.
225    #[inline]
226    pub fn decouple(self) -> (Ref<T::Base, Own>, T::UserData) {
227        (self.owner, self.script)
228    }
229
230    /// Returns a reference to the base object.
231    #[inline]
232    pub fn base(&self) -> &Ref<T::Base, Own> {
233        &self.owner
234    }
235
236    /// Returns a reference to the script wrapper.
237    #[inline]
238    pub fn script(&self) -> &T::UserData {
239        &self.script
240    }
241
242    /// Convert to a nullable raw pointer. Used for AsArg.
243    pub(super) fn as_base_ptr(&self) -> *mut sys::godot_object {
244        self.owner.as_ptr()
245    }
246}
247
248impl<T: NativeClass, Own: Ownership> Instance<T, Own>
249where
250    RefImplBound: SafeAsRaw<<T::Base as GodotObject>::Memory, Own>,
251{
252    /// Try to downcast `Ref<T::Base, Own>` to `Instance<T>`, without changing the reference
253    /// count if reference-counted.
254    ///
255    /// # Errors
256    ///
257    /// Returns the original `Ref` if the cast failed.
258    #[inline]
259    pub fn try_from_base(owner: Ref<T::Base, Own>) -> Result<Self, Ref<T::Base, Own>> {
260        let user_data = match try_get_user_data_ptr::<T>(owner.as_raw()) {
261            Some(user_data) => user_data,
262            None => return Err(owner),
263        };
264
265        let script = unsafe { T::UserData::clone_from_user_data_unchecked(user_data) };
266
267        Ok(Instance { owner, script })
268    }
269
270    /// Try to downcast `Ref<T::Base, Own>` to `Instance<T>`, without changing the reference
271    /// count if reference-counted. Shorthand for `Self::try_from_base().ok()`.
272    #[inline]
273    pub fn from_base(owner: Ref<T::Base, Own>) -> Option<Self> {
274        Self::try_from_base(owner).ok()
275    }
276}
277
278impl<T: NativeClass, Own: Ownership> Instance<T, Own>
279where
280    RefImplBound: SafeDeref<<T::Base as GodotObject>::Memory, Own>,
281{
282    /// Calls a function with a NativeClass instance and its owner, and returns its return
283    /// value. Can be used on reference counted types for multiple times.
284    #[inline]
285    pub fn map<F, U>(&self, op: F) -> Result<U, <T::UserData as Map>::Err>
286    where
287        T::UserData: Map,
288        F: FnOnce(&T, TRef<'_, T::Base, Own>) -> U,
289    {
290        self.script.map(|script| op(script, self.owner.as_ref()))
291    }
292
293    /// Calls a function with a NativeClass instance and its owner, and returns its return
294    /// value. Can be used on reference counted types for multiple times.
295    #[inline]
296    pub fn map_mut<F, U>(&self, op: F) -> Result<U, <T::UserData as MapMut>::Err>
297    where
298        T::UserData: MapMut,
299        F: FnOnce(&mut T, TRef<'_, T::Base, Own>) -> U,
300    {
301        self.script
302            .map_mut(|script| op(script, self.owner.as_ref()))
303    }
304
305    /// Calls a function with a NativeClass instance and its owner, and returns its return
306    /// value. Can be used on reference counted types for multiple times.
307    #[inline]
308    pub fn map_owned<F, U>(&self, op: F) -> Result<U, <T::UserData as MapOwned>::Err>
309    where
310        T::UserData: MapOwned,
311        F: FnOnce(T, TRef<'_, T::Base, Own>) -> U,
312    {
313        self.script
314            .map_owned(|script| op(script, self.owner.as_ref()))
315    }
316}
317
318/// Methods for instances with manually-managed base classes.
319impl<T: NativeClass> Instance<T, Shared> {
320    /// Assume that `self` is safe to use.
321    ///
322    /// This is *not* guaranteed to be a no-op at runtime.
323    ///
324    /// # Safety
325    ///
326    /// It's safe to call `assume_safe` only if the constraints of `Ref::assume_safe`
327    /// are satisfied for the base object.
328    #[inline]
329    pub unsafe fn assume_safe<'a, 'r>(&'r self) -> TInstance<'a, T, Shared>
330    where
331        AssumeSafeLifetime<'a, 'r>: LifetimeConstraint<<T::Base as GodotObject>::Memory>,
332    {
333        TInstance {
334            owner: self.owner.assume_safe(),
335            script: self.script.clone(),
336        }
337    }
338}
339
340impl<T: NativeClass> Instance<T, Shared>
341where
342    T::Base: GodotObject<Memory = ManuallyManaged>,
343{
344    /// Returns `true` if the pointer currently points to a valid object of the correct type.
345    /// **This does NOT guarantee that it's safe to use this pointer.**
346    ///
347    /// # Safety
348    ///
349    /// This thread must have exclusive access to the object during the call.
350    #[inline]
351    #[allow(clippy::trivially_copy_pass_by_ref)]
352    pub unsafe fn is_instance_sane(&self) -> bool {
353        self.owner.is_instance_sane()
354    }
355}
356
357impl<T: NativeClass> Instance<T, Unique>
358where
359    T::Base: GodotObject<Memory = ManuallyManaged>,
360{
361    /// Frees the base object and user-data wrapper.
362    ///
363    /// Same as `self.into_base().free()`.
364    #[inline]
365    pub fn free(self) {
366        self.into_base().free()
367    }
368}
369
370impl<T: NativeClass> Instance<T, Unique>
371where
372    T::Base: GodotObject<Memory = ManuallyManaged> + QueueFree,
373{
374    /// Queues the base object and user-data wrapper for deallocation in the near future.
375    /// This should be preferred to `free` for `Node`s.
376    ///
377    /// Same as `self.into_base().queue_free()`.
378    #[inline]
379    pub fn queue_free(self) {
380        self.into_base().queue_free()
381    }
382}
383
384impl<T: NativeClass> Instance<T, Unique> {
385    /// Coverts into a `Shared` instance.
386    #[inline]
387    pub fn into_shared(self) -> Instance<T, Shared> {
388        Instance {
389            owner: self.owner.into_shared(),
390            script: self.script,
391        }
392    }
393}
394
395impl<T: NativeClass> Instance<T, Unique>
396where
397    T::Base: GodotObject<Memory = RefCounted>,
398{
399    /// Coverts into a `ThreadLocal` instance.
400    #[inline]
401    pub fn into_thread_local(self) -> Instance<T, ThreadLocal> {
402        Instance {
403            owner: self.owner.into_thread_local(),
404            script: self.script,
405        }
406    }
407}
408
409impl<T: NativeClass> Instance<T, Shared> {
410    /// Assume that `self` is the unique reference to the underlying base object.
411    ///
412    /// This is guaranteed to be a no-op at runtime if `debug_assertions` is disabled. Runtime
413    /// sanity checks may be added in debug builds to help catch bugs.
414    ///
415    /// # Safety
416    ///
417    /// Calling `assume_unique` when `self` isn't the unique reference is instant undefined
418    /// behavior. This is a much stronger assumption than `assume_safe` and should be used with
419    /// care.
420    #[inline]
421    pub unsafe fn assume_unique(self) -> Instance<T, Unique> {
422        Instance {
423            owner: self.owner.assume_unique(),
424            script: self.script,
425        }
426    }
427}
428
429impl<T: NativeClass> Instance<T, Shared>
430where
431    T::Base: GodotObject<Memory = RefCounted>,
432{
433    /// Assume that all references to the underlying object is local to the current thread.
434    ///
435    /// This is guaranteed to be a no-op at runtime.
436    ///
437    /// # Safety
438    ///
439    /// Calling `assume_thread_local` when there are references on other threads is instant
440    /// undefined behavior. This is a much stronger assumption than `assume_safe` and should
441    /// be used with care.
442    #[inline]
443    pub unsafe fn assume_thread_local(self) -> Instance<T, ThreadLocal> {
444        Instance {
445            owner: self.owner.assume_thread_local(),
446            script: self.script,
447        }
448    }
449}
450
451impl<'a, T: NativeClass, Own: Ownership> TInstance<'a, T, Own> {
452    /// Returns a reference to the base object with the same lifetime.
453    #[inline]
454    pub fn base(&self) -> TRef<'a, T::Base, Own> {
455        self.owner
456    }
457
458    /// Returns a reference to the script wrapper.
459    #[inline]
460    pub fn script(&self) -> &T::UserData {
461        &self.script
462    }
463
464    /// Try to downcast `TRef<'a, T::Base, Own>` to `TInstance<T>`.
465    #[inline]
466    pub fn try_from_base(owner: TRef<'a, T::Base, Own>) -> Option<Self> {
467        let user_data = try_get_user_data_ptr::<T>(owner.as_raw())?;
468        unsafe { Some(Self::from_raw_unchecked(owner, user_data)) }
469    }
470
471    /// Pairs an `owner` and `user_data` without checking validity. Internal interface.
472    #[doc(hidden)]
473    #[inline]
474    pub unsafe fn from_raw_unchecked(
475        owner: TRef<'a, T::Base, Own>,
476        user_data: *mut libc::c_void,
477    ) -> Self {
478        let script = T::UserData::clone_from_user_data_unchecked(user_data);
479        TInstance { owner, script }
480    }
481
482    /// Convert to a nullable raw pointer. Used for AsArg.
483    pub(super) fn as_base_ptr(&self) -> *mut sys::godot_object {
484        self.owner.as_ptr()
485    }
486}
487
488impl<'a, T: NativeClass, Own: NonUniqueOwnership> TInstance<'a, T, Own> {
489    /// Persists this into a persistent `Instance` with the same thread access, without cloning
490    /// the userdata wrapper.
491    ///
492    /// This is only available for non-`Unique` accesses.
493    #[inline]
494    pub fn claim(self) -> Instance<T, Own> {
495        Instance {
496            owner: self.owner.claim(),
497            script: self.script,
498        }
499    }
500}
501
502/// Methods for instances with reference-counted base classes.
503impl<'a, T: NativeClass, Own: Ownership> TInstance<'a, T, Own>
504where
505    T::Base: GodotObject,
506{
507    /// Calls a function with a NativeClass instance and its owner, and returns its return
508    /// value.
509    #[inline]
510    pub fn map<F, U>(&self, op: F) -> Result<U, <T::UserData as Map>::Err>
511    where
512        T::UserData: Map,
513        F: FnOnce(&T, TRef<'_, T::Base, Own>) -> U,
514    {
515        self.script.map(|script| op(script, self.owner))
516    }
517
518    /// Calls a function with a NativeClass instance and its owner, and returns its return
519    /// value.
520    #[inline]
521    pub fn map_mut<F, U>(&self, op: F) -> Result<U, <T::UserData as MapMut>::Err>
522    where
523        T::UserData: MapMut,
524        F: FnOnce(&mut T, TRef<'_, T::Base, Own>) -> U,
525    {
526        self.script.map_mut(|script| op(script, self.owner))
527    }
528
529    /// Calls a function with a NativeClass instance and its owner, and returns its return
530    /// value.
531    #[inline]
532    pub fn map_owned<F, U>(&self, op: F) -> Result<U, <T::UserData as MapOwned>::Err>
533    where
534        T::UserData: MapOwned,
535        F: FnOnce(T, TRef<'_, T::Base, Own>) -> U,
536    {
537        self.script.map_owned(|script| op(script, self.owner))
538    }
539}
540
541impl<T, Own: Ownership> Clone for Instance<T, Own>
542where
543    T: NativeClass,
544    Ref<T::Base, Own>: Clone,
545{
546    #[inline]
547    fn clone(&self) -> Self {
548        Instance {
549            owner: self.owner.clone(),
550            script: self.script.clone(),
551        }
552    }
553}
554
555impl<'a, T, Own: Ownership> Clone for TInstance<'a, T, Own>
556where
557    T: NativeClass,
558{
559    #[inline]
560    fn clone(&self) -> Self {
561        TInstance {
562            owner: self.owner,
563            script: self.script.clone(),
564        }
565    }
566}
567
568impl<T, Own: Ownership> ToVariant for Instance<T, Own>
569where
570    T: NativeClass,
571    Ref<T::Base, Own>: ToVariant,
572{
573    #[inline]
574    fn to_variant(&self) -> Variant {
575        self.owner.to_variant()
576    }
577}
578
579impl<T> OwnedToVariant for Instance<T, Unique>
580where
581    T: NativeClass,
582{
583    #[inline]
584    fn owned_to_variant(self) -> Variant {
585        self.into_base().owned_to_variant()
586    }
587}
588
589impl<T> FromVariant for Instance<T, Shared>
590where
591    T: NativeClass,
592    T::Base: GodotObject<Memory = RefCounted>,
593{
594    #[inline]
595    fn from_variant(variant: &Variant) -> Result<Self, FromVariantError> {
596        let owner = Ref::<T::Base, Shared>::from_variant(variant)?;
597        Self::from_base(owner).ok_or(FromVariantError::InvalidInstance {
598            expected: class_registry::class_name_or_default::<T>(),
599        })
600    }
601}
602
603fn try_get_user_data_ptr<T: NativeClass>(owner: &RawObject<T::Base>) -> Option<*mut libc::c_void> {
604    unsafe {
605        let api = get_api();
606
607        let owner_ptr = owner.sys().as_ptr();
608
609        let type_tag = (api.godot_nativescript_get_type_tag)(owner_ptr);
610        if type_tag.is_null() {
611            return None;
612        }
613
614        if !crate::export::type_tag::check::<T>(type_tag) {
615            return None;
616        }
617
618        Some((api.godot_nativescript_get_userdata)(owner_ptr))
619    }
620}