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#[derive(Debug)]
23pub struct Instance<T: NativeClass, Own: Ownership = Shared> {
24 owner: Ref<T::Base, Own>,
25 script: T::UserData,
26}
27
28#[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 #[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 #[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 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 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 #[inline]
214 pub fn into_base(self) -> Ref<T::Base, Own> {
215 self.owner
216 }
217
218 #[inline]
220 pub fn into_script(self) -> T::UserData {
221 self.script
222 }
223
224 #[inline]
226 pub fn decouple(self) -> (Ref<T::Base, Own>, T::UserData) {
227 (self.owner, self.script)
228 }
229
230 #[inline]
232 pub fn base(&self) -> &Ref<T::Base, Own> {
233 &self.owner
234 }
235
236 #[inline]
238 pub fn script(&self) -> &T::UserData {
239 &self.script
240 }
241
242 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 #[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 #[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 #[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 #[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 #[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
318impl<T: NativeClass> Instance<T, Shared> {
320 #[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 #[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 #[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 #[inline]
379 pub fn queue_free(self) {
380 self.into_base().queue_free()
381 }
382}
383
384impl<T: NativeClass> Instance<T, Unique> {
385 #[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 #[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 #[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 #[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 #[inline]
454 pub fn base(&self) -> TRef<'a, T::Base, Own> {
455 self.owner
456 }
457
458 #[inline]
460 pub fn script(&self) -> &T::UserData {
461 &self.script
462 }
463
464 #[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 #[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 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 #[inline]
494 pub fn claim(self) -> Instance<T, Own> {
495 Instance {
496 owner: self.owner.claim(),
497 script: self.script,
498 }
499 }
500}
501
502impl<'a, T: NativeClass, Own: Ownership> TInstance<'a, T, Own>
504where
505 T::Base: GodotObject,
506{
507 #[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 #[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 #[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}