godot_core/obj/
script.rs

1/*
2 * Copyright (c) godot-rust; Bromeon and contributors.
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
6 */
7
8//! Functionality related to script instances (Rust code that can be attached as node scripts).
9//!
10//! The features in this module are complemented by the [`ScriptExtension` class][crate::classes::ScriptExtension] and
11//! the [`IScriptExtension` trait][crate::classes::IScriptExtension].
12//!
13//! See [`ScriptInstance`](trait.ScriptInstance.html) for usage.
14
15// Re-export guards.
16pub use crate::obj::guards::{ScriptBaseMut, ScriptBaseRef};
17
18use std::ffi::c_void;
19use std::ops::{Deref, DerefMut};
20
21#[cfg(not(feature = "experimental-threads"))] #[cfg_attr(published_docs, doc(cfg(not(feature = "experimental-threads"))))]
22use godot_cell::panicking::{GdCell, MutGuard, RefGuard};
23
24#[cfg(feature = "experimental-threads")] #[cfg_attr(published_docs, doc(cfg(feature = "experimental-threads")))]
25use godot_cell::blocking::{GdCell, MutGuard, RefGuard};
26
27use crate::builtin::{GString, StringName, Variant, VariantType};
28use crate::classes::{Object, Script, ScriptLanguage};
29use crate::meta::{MethodInfo, PropertyInfo};
30use crate::obj::{Base, Gd, GodotClass};
31use crate::sys;
32
33#[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
34use self::bounded_ptr_list::BoundedPtrList;
35
36#[cfg(since_api = "4.2")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.2")))]
37use crate::classes::IScriptExtension;
38#[cfg(since_api = "4.2")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.2")))]
39use crate::obj::Inherits;
40
41/// Implement custom scripts that can be attached to objects in Godot.
42///
43/// To use script instances, implement this trait for your own type.
44///
45/// You can use the [`create_script_instance()`] function to create a low-level pointer to your script instance. This pointer should then be
46/// returned from [`IScriptExtension::instance_create_rawptr()`](crate::classes::IScriptExtension::instance_create_rawptr).
47///
48/// # Example
49///
50/// ```no_run
51/// # // Trick 17 to avoid listing all the methods. Needs also a method.
52/// # mod godot {
53/// #     pub use ::godot::*;
54/// #     pub mod extras { pub trait ScriptInstance {} pub trait IScriptExtension {} }
55/// # }
56/// # fn create_script_instance(_: MyInstance) -> *mut std::ffi::c_void { std::ptr::null_mut() }
57/// use godot::prelude::*;
58/// use godot::classes::{Script, ScriptExtension};
59/// use godot::extras::{IScriptExtension, ScriptInstance};
60///
61/// // 1) Define the script.
62/// // This needs #[class(tool)] since the script extension runs in the editor.
63/// #[derive(GodotClass)]
64/// #[class(init, base=ScriptExtension, tool)]
65/// struct MyScript {
66///    base: Base<ScriptExtension>,
67///    // ... other fields
68/// }
69///
70/// // 2) Define the script _instance_, and implement the trait for it.
71/// struct MyInstance;
72/// impl MyInstance {
73///     fn from_gd(script: Gd<Script>) -> Self {
74///         Self { /* ... */ }
75///     }
76/// }
77///
78/// impl ScriptInstance for MyInstance {
79///     // Implement all the methods...
80/// }
81///
82/// // 3) Implement the script's virtual interface to wire up 1) and 2).
83/// #[godot_api]
84/// impl IScriptExtension for MyScript {
85///     // Implement all the methods...
86/// }
87/// ```
88pub trait ScriptInstance: Sized {
89    type Base: GodotClass;
90
91    /// Name of the new class the script implements.
92    fn class_name(&self) -> GString;
93
94    /// Property setter for Godot's virtual dispatch system.
95    ///
96    /// The engine will call this function when it wants to change a property on the script.
97    fn set_property(this: SiMut<Self>, name: StringName, value: &Variant) -> bool;
98
99    /// Property getter for Godot's virtual dispatch system.
100    ///
101    /// The engine will call this function when it wants to read a property on the script.
102    fn get_property(&self, name: StringName) -> Option<Variant>;
103
104    /// A list of all the properties a script exposes to the engine.
105    fn get_property_list(&self) -> Vec<PropertyInfo>;
106
107    /// A list of all the methods a script exposes to the engine.
108    fn get_method_list(&self) -> Vec<MethodInfo>;
109
110    /// Method invoker for Godot's virtual dispatch system. The engine will call this function when it wants to call a method on the script.
111    ///
112    /// All method calls are taking a mutable reference of the script instance, as the engine does not differentiate between immutable and
113    /// mutable method calls like rust.
114    ///
115    /// It's important that the script does not cause a second call to this function while executing a method call. This would result in a panic.
116    // TODO: map the sys::GDExtensionCallErrorType to some public API type.
117    fn call(
118        this: SiMut<Self>,
119        method: StringName,
120        args: &[&Variant],
121    ) -> Result<Variant, sys::GDExtensionCallErrorType>;
122
123    /// Identifies the script instance as a placeholder, routing property writes to a fallback if applicable.
124    ///
125    /// If this function and [IScriptExtension::is_placeholder_fallback_enabled] return true, Godot will call [`Self::property_set_fallback`]
126    /// instead of [`Self::set_property`].
127    fn is_placeholder(&self) -> bool;
128
129    /// Validation function for the engine to verify if the script exposes a certain method.
130    fn has_method(&self, method: StringName) -> bool;
131
132    /// Lets the engine get a reference to the script this instance was created for.
133    ///
134    /// This function has to return a reference, because scripts are reference-counted in Godot, and it must be guaranteed that the object is
135    /// not freed before the engine increased the reference count. (Every time a ref-counted `Gd<T>` is dropped, the reference count is
136    /// decremented.)
137    fn get_script(&self) -> &Gd<Script>;
138
139    /// Lets the engine fetch the type of a particular property.
140    fn get_property_type(&self, name: StringName) -> VariantType;
141
142    /// String representation of the script instance.
143    fn to_string(&self) -> GString;
144
145    /// A dump of all property names and values that are exposed to the engine.
146    fn get_property_state(&self) -> Vec<(StringName, Variant)>;
147
148    /// Lets the engine get a reference to the [`ScriptLanguage`] this instance belongs to.
149    fn get_language(&self) -> Gd<ScriptLanguage>;
150
151    /// Callback from the engine when the reference count of the base object has been decreased. When this method returns `true` the engine will
152    /// not free the object the script is attached to.
153    fn on_refcount_decremented(&self) -> bool;
154
155    /// Callback from the engine when the reference count of the base object has been increased.
156    fn on_refcount_incremented(&self);
157
158    /// The engine may call this function if it failed to get a property value via [`ScriptInstance::get_property`] or the native type's getter.
159    fn property_get_fallback(&self, name: StringName) -> Option<Variant>;
160
161    /// The engine may call this function if [`IScriptExtension::is_placeholder_fallback_enabled`] is enabled.
162    fn property_set_fallback(this: SiMut<Self>, name: StringName, value: &Variant) -> bool;
163
164    /// This function will be called to handle calls to [`Object::get_method_argument_count`](crate::classes::Object::get_method_argument_count)
165    /// and `Callable::get_argument_count`.
166    ///
167    /// If `None` is returned the public methods will return `0`.
168    #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
169    fn get_method_argument_count(&self, _method: StringName) -> Option<u32>;
170}
171
172#[cfg(before_api = "4.2")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.2")))]
173type ScriptInstanceInfo = sys::GDExtensionScriptInstanceInfo;
174#[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"))))]
175type ScriptInstanceInfo = sys::GDExtensionScriptInstanceInfo2;
176#[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
177type ScriptInstanceInfo = sys::GDExtensionScriptInstanceInfo3;
178
179struct ScriptInstanceData<T: ScriptInstance> {
180    inner: GdCell<T>,
181    script_instance_ptr: *mut ScriptInstanceInfo,
182    #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
183    property_lists: BoundedPtrList<sys::GDExtensionPropertyInfo>,
184    #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
185    method_lists: BoundedPtrList<sys::GDExtensionMethodInfo>,
186    base: Base<T::Base>,
187}
188
189impl<T: ScriptInstance> ScriptInstanceData<T> {
190    ///  Convert a `ScriptInstanceData` sys pointer to a reference with unbounded lifetime.
191    ///
192    /// # Safety
193    ///
194    /// `ptr` must point to a live `ScriptInstanceData<T>` for the duration of `'a`.
195    unsafe fn borrow_script_sys<'a>(ptr: sys::GDExtensionScriptInstanceDataPtr) -> &'a Self {
196        &*(ptr.cast::<ScriptInstanceData<T>>())
197    }
198
199    fn borrow(&self) -> RefGuard<'_, T> {
200        self.inner
201            .borrow()
202            .unwrap_or_else(|err| Self::borrow_panic(err))
203    }
204
205    fn borrow_mut(&self) -> MutGuard<'_, T> {
206        self.inner
207            .borrow_mut()
208            .unwrap_or_else(|err| Self::borrow_panic(err))
209    }
210
211    fn cell_ref(&self) -> &GdCell<T> {
212        &self.inner
213    }
214
215    fn borrow_panic(err: Box<dyn std::error::Error>) -> ! {
216        panic!(
217            "\
218                ScriptInstance borrow failed, already bound; T = {}.\n  \
219                Make sure to use `SiMut::base_mut()` when possible.\n  \
220                Details: {err}.\
221            ",
222            std::any::type_name::<T>(),
223        )
224    }
225}
226
227impl<T: ScriptInstance> Drop for ScriptInstanceData<T> {
228    fn drop(&mut self) {
229        // SAFETY: The ownership of ScriptInstanceData is transferred to Godot after its creation. The engine then calls
230        // script_instance_info::free_func when it frees its own GDExtensionScriptInstance and subsequently wants to drop the ScriptInstanceData.
231        // After the data has been dropped, the instance info is no longer being used, but never freed. It is therefore safe to drop the
232        // instance info at the same time.
233        let instance = unsafe { Box::from_raw(self.script_instance_ptr) };
234
235        drop(instance);
236    }
237}
238
239/// Creates a new  from a type that implements [`ScriptInstance`].
240///
241/// See [`ScriptInstance`] for usage. Discarding the resulting value will result in a memory leak.
242///
243/// The exact GDExtension type of the pointer is `sys::GDExtensionScriptInstancePtr`, but you can treat it like an opaque pointer.
244///
245/// # Safety
246/// The caller must ensure that `for_object` is not freed before passing the returned pointer back to Godot.
247#[must_use]
248pub unsafe fn create_script_instance<T: ScriptInstance>(
249    rust_instance: T,
250    for_object: Gd<T::Base>,
251) -> *mut c_void {
252    // Field grouping matches C header.
253    let gd_instance = ScriptInstanceInfo {
254        set_func: Some(script_instance_info::set_property_func::<T>),
255        get_func: Some(script_instance_info::get_property_func::<T>),
256        get_property_list_func: Some(script_instance_info::get_property_list_func::<T>),
257        #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
258        free_property_list_func: Some(script_instance_info::free_property_list_func::<T>),
259        #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
260        free_property_list_func: Some(script_instance_info::free_property_list_func),
261
262        #[cfg(since_api = "4.2")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.2")))]
263        get_class_category_func: None, // not yet implemented.
264
265        property_can_revert_func: None, // unimplemented until needed.
266        property_get_revert_func: None, // unimplemented until needed.
267
268        // ScriptInstance::get_owner() is apparently not called by Godot 4.1 to 4.2 (to verify).
269        get_owner_func: None,
270        get_property_state_func: Some(script_instance_info::get_property_state_func::<T>),
271
272        get_method_list_func: Some(script_instance_info::get_method_list_func::<T>),
273        #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
274        free_method_list_func: Some(script_instance_info::free_method_list_func::<T>),
275        #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
276        free_method_list_func: Some(script_instance_info::free_method_list_func),
277        get_property_type_func: Some(script_instance_info::get_property_type_func::<T>),
278        #[cfg(since_api = "4.2")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.2")))]
279        validate_property_func: None, // not yet implemented.
280
281        has_method_func: Some(script_instance_info::has_method_func::<T>),
282
283        call_func: Some(script_instance_info::call_func::<T>),
284        notification_func: None, // not yet implemented.
285
286        to_string_func: Some(script_instance_info::to_string_func::<T>),
287
288        refcount_incremented_func: Some(script_instance_info::refcount_incremented_func::<T>),
289        refcount_decremented_func: Some(script_instance_info::refcount_decremented_func::<T>),
290
291        get_script_func: Some(script_instance_info::get_script_func::<T>),
292
293        is_placeholder_func: Some(script_instance_info::is_placeholder_func::<T>),
294
295        get_fallback_func: Some(script_instance_info::get_fallback_func::<T>),
296        set_fallback_func: Some(script_instance_info::set_fallback_func::<T>),
297
298        get_language_func: Some(script_instance_info::get_language_func::<T>),
299
300        free_func: Some(script_instance_info::free_func::<T>),
301
302        #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
303        get_method_argument_count_func: Some(
304            script_instance_info::get_method_argument_count_func::<T>,
305        ),
306    };
307
308    let instance_ptr = Box::into_raw(Box::new(gd_instance));
309
310    let data = ScriptInstanceData {
311        inner: GdCell::new(rust_instance),
312        script_instance_ptr: instance_ptr,
313        #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
314        property_lists: BoundedPtrList::new(),
315        #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
316        method_lists: BoundedPtrList::new(),
317        // SAFETY: The script instance is always freed before the base object is destroyed. The weak reference should therefore never be
318        // accessed after it has been freed.
319        base: unsafe { Base::from_gd(&for_object) },
320    };
321
322    let data_ptr = Box::into_raw(Box::new(data));
323
324    // SAFETY: `script_instance_create` expects a `GDExtensionScriptInstanceInfoPtr` and a generic `GDExtensionScriptInstanceDataPtr` of our
325    // choice. The validity of the instance info struct is ensured by code generation.
326    //
327    // It is expected that the engine upholds the safety invariants stated on each of the GDEXtensionScriptInstanceInfo functions.
328    unsafe {
329        #[cfg(before_api = "4.2")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.2")))]
330        let create_fn = sys::interface_fn!(script_instance_create);
331
332        #[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"))))]
333        let create_fn = sys::interface_fn!(script_instance_create2);
334
335        #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
336        let create_fn = sys::interface_fn!(script_instance_create3);
337
338        create_fn(
339            instance_ptr,
340            data_ptr as sys::GDExtensionScriptInstanceDataPtr,
341        ) as *mut c_void
342    }
343}
344
345/// Checks if an instance of the script exists for a given object.
346///
347/// This function both checks if the passed script matches the one currently assigned to the passed object, as well as verifies that
348/// there is an instance for the script.
349///
350/// Use this function to implement [`IScriptExtension::instance_has`].
351#[cfg(since_api = "4.2")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.2")))]
352pub fn script_instance_exists<O, S>(object: &Gd<O>, script: &Gd<S>) -> bool
353where
354    O: Inherits<Object>,
355    S: Inherits<Script> + IScriptExtension + super::Bounds<Declarer = super::bounds::DeclUser>,
356{
357    let object_script_variant = object.upcast_ref().get_script();
358
359    if object_script_variant.is_nil() {
360        return false;
361    }
362
363    if object_script_variant
364        .object_id()
365        .is_none_or(|instance_id| instance_id != script.instance_id())
366    {
367        return false;
368    }
369
370    let Some(language) = script.bind().get_language() else {
371        return false;
372    };
373
374    let get_instance_fn = sys::interface_fn!(object_get_script_instance);
375
376    // SAFETY: Object and language are alive and their sys pointers are valid.
377    let instance = unsafe { get_instance_fn(object.obj_sys(), language.obj_sys()) };
378
379    !instance.is_null()
380}
381
382/// Mutable/exclusive reference guard for a `T` where `T` implements [`ScriptInstance`].
383///
384/// This can be used to access the base object of a [`ScriptInstance`], which in turn can be used to make reentrant calls to engine APIs.
385/// For details see [`SiMut::base_mut()`].
386pub struct SiMut<'a, T: ScriptInstance> {
387    mut_ref: &'a mut T,
388    cell: &'a GdCell<T>,
389    base_ref: &'a Base<T::Base>,
390}
391
392impl<'a, T: ScriptInstance> SiMut<'a, T> {
393    fn new(
394        cell: &'a GdCell<T>,
395        cell_guard: &'a mut MutGuard<T>,
396        base_ref: &'a Base<T::Base>,
397    ) -> Self {
398        let mut_ref = cell_guard.deref_mut();
399
400        Self {
401            mut_ref,
402            cell,
403            base_ref,
404        }
405    }
406
407    /// Returns a shared reference suitable for calling engine methods on this object.
408    ///
409    /// Holding a shared guard prevents other code paths from obtaining a _mutable_ reference to `self`, as such it is recommended to drop the
410    /// guard as soon as you no longer need it.
411    ///
412    /// ```no_run
413    /// # use godot::prelude::*;
414    /// # use godot::classes::{ScriptLanguage, Script};
415    /// # use godot::obj::script::{ScriptInstance, SiMut};
416    /// # use godot::meta::{MethodInfo, PropertyInfo};
417    /// # use godot::sys;
418    /// struct ExampleScriptInstance;
419    ///
420    /// impl ScriptInstance for ExampleScriptInstance {
421    ///     type Base = Node;
422    ///
423    ///     fn call(
424    ///         this: SiMut<Self>,
425    ///         method: StringName,
426    ///         args: &[&Variant],
427    ///     ) -> Result<Variant, sys::GDExtensionCallErrorType>{
428    ///         let name = this.base().get_name();
429    ///         godot_print!("name is {name}");
430    ///         // However, we cannot call methods that require `&mut Base`, such as:
431    ///         // this.base().add_child(&node);
432    ///         Ok(Variant::nil())
433    ///     }
434    ///     # fn class_name(&self) -> GString { todo!() }
435    ///     # fn set_property(_: SiMut<'_, Self>, _: StringName, _: &Variant) -> bool { todo!() }
436    ///     # fn get_property(&self, _: StringName) -> Option<Variant> { todo!() }
437    ///     # fn get_property_list(&self) -> Vec<PropertyInfo> { todo!() }
438    ///     # fn get_method_list(&self) -> Vec<MethodInfo> { todo!() }
439    ///     # fn is_placeholder(&self) -> bool { todo!() }
440    ///     # fn has_method(&self, _: StringName) -> bool { todo!() }
441    ///     # fn get_script(&self) -> &Gd<Script> { todo!() }
442    ///     # fn get_property_type(&self, _: StringName) -> VariantType { todo!() }
443    ///     # fn to_string(&self) -> GString { todo!() }
444    ///     # fn get_property_state(&self) -> Vec<(StringName, Variant)> { todo!() }
445    ///     # fn get_language(&self) -> Gd<ScriptLanguage> { todo!() }
446    ///     # fn on_refcount_decremented(&self) -> bool { todo!() }
447    ///     # fn on_refcount_incremented(&self) { todo!() }
448    ///     # fn property_get_fallback(&self, _: StringName) -> Option<Variant> { todo!() }
449    ///     # fn property_set_fallback(_: SiMut<'_, Self>, _: StringName, _: &Variant) -> bool { todo!() }
450    ///     # fn get_method_argument_count(&self, _: StringName) -> Option<u32> { todo!() }
451    /// }
452    /// ```
453    pub fn base(&self) -> ScriptBaseRef<T> {
454        ScriptBaseRef::new(self.base_ref.to_gd(), self.mut_ref)
455    }
456
457    /// Returns a mutable reference suitable for calling engine methods on this object.
458    ///
459    /// This method will allow you to call back into the same object from Godot (re-entrancy).
460    /// You have to keep the `ScriptBaseRef` guard bound for the entire duration the engine might re-enter a function of your
461    /// `ScriptInstance`. The guard temporarily absorbs the `&mut self` reference, which allows for an additional mutable reference to be
462    /// acquired.
463    ///
464    /// Holding a mutable guard prevents other code paths from obtaining _any_ reference to `self`, as such it is recommended to drop the
465    /// guard as soon as you no longer need it.
466    ///
467    /// ```no_run
468    /// # use godot::prelude::*;
469    /// # use godot::classes::{ScriptLanguage, Script};
470    /// # use godot::obj::script::{ScriptInstance, SiMut};
471    /// # use godot::meta::{MethodInfo, PropertyInfo};
472    /// # use godot::sys;
473    /// struct ExampleScriptInstance;
474    ///
475    /// impl ScriptInstance for ExampleScriptInstance {
476    ///     type Base = Object;
477    ///
478    ///     fn call(
479    ///         mut this: SiMut<Self>,
480    ///         method: StringName,
481    ///         args: &[&Variant],
482    ///     ) -> Result<Variant, sys::GDExtensionCallErrorType> {
483    ///         // Check whether method is available on this script
484    ///         if method == StringName::from("script_method") {
485    ///             godot_print!("script_method called!");
486    ///             return Ok(true.to_variant());
487    ///         }
488    ///
489    ///         let node = Node::new_alloc();
490    ///
491    ///         // We can call back into `self` through Godot:
492    ///         this.base_mut().call("script_method", &[]);
493    ///
494    ///         Ok(Variant::nil())
495    ///     }
496    ///     # fn class_name(&self) -> GString { todo!() }
497    ///     # fn set_property(_: SiMut<'_, Self>, _: StringName, _: &Variant) -> bool { todo!() }
498    ///     # fn get_property(&self, _: StringName) -> Option<Variant> { todo!() }
499    ///     # fn get_property_list(&self) -> Vec<PropertyInfo> { todo!() }
500    ///     # fn get_method_list(&self) -> Vec<MethodInfo> { todo!() }
501    ///     # fn is_placeholder(&self) -> bool { todo!() }
502    ///     # fn has_method(&self, _: StringName) -> bool { todo!() }
503    ///     # fn get_script(&self) -> &Gd<Script> { todo!() }
504    ///     # fn get_property_type(&self, _: StringName) -> VariantType { todo!() }
505    ///     # fn to_string(&self) -> GString { todo!() }
506    ///     # fn get_property_state(&self) -> Vec<(StringName, Variant)> { todo!() }
507    ///     # fn get_language(&self) -> Gd<ScriptLanguage> { todo!() }
508    ///     # fn on_refcount_decremented(&self) -> bool { todo!() }
509    ///     # fn on_refcount_incremented(&self) { todo!() }
510    ///     # fn property_get_fallback(&self, _: StringName) -> Option<Variant> { todo!() }
511    ///     # fn property_set_fallback(_: SiMut<'_, Self>, _: StringName, _: &Variant) -> bool { todo!() }
512    ///     # fn get_method_argument_count(&self, _: StringName) -> Option<u32> { todo!() }
513    /// }
514    /// ```
515    pub fn base_mut(&mut self) -> ScriptBaseMut<T> {
516        let guard = self.cell.make_inaccessible(self.mut_ref).unwrap();
517
518        ScriptBaseMut::new(self.base_ref.to_gd(), guard)
519    }
520}
521
522impl<T: ScriptInstance> Deref for SiMut<'_, T> {
523    type Target = T;
524
525    fn deref(&self) -> &Self::Target {
526        self.mut_ref
527    }
528}
529
530impl<T: ScriptInstance> DerefMut for SiMut<'_, T> {
531    fn deref_mut(&mut self) -> &mut Self::Target {
532        self.mut_ref
533    }
534}
535
536// Encapsulate BoundedPtrList to help ensure safety.
537#[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
538mod bounded_ptr_list {
539    use std::collections::HashMap;
540    use std::sync::Mutex;
541
542    use godot_ffi as sys;
543
544    /// Helper struct to store the lengths of lists, so they can be properly freed.
545    ///
546    /// This uses the term `list` because it refers to property/method lists in gdextension.
547    pub struct BoundedPtrList<T> {
548        list_lengths: Mutex<HashMap<*mut T, u32>>,
549    }
550
551    impl<T> BoundedPtrList<T> {
552        pub fn new() -> Self {
553            Self {
554                list_lengths: Mutex::new(HashMap::new()),
555            }
556        }
557
558        /// Convert a list into a pointer + length pair. Should be used together with [`list_from_sys`](Self::list_from_sys).
559        ///
560        /// If `list_from_sys` is not called on this list then that will cause a memory leak.
561        pub fn list_into_sys(&self, list: Vec<T>) -> (*const T, u32) {
562            let len: u32 = list
563                .len()
564                .try_into()
565                .expect("list must have length that fits in u32");
566            let ptr = Box::leak(list.into_boxed_slice()).as_mut_ptr();
567
568            let old_value = self.list_lengths.lock().unwrap().insert(ptr, len);
569            assert_eq!(
570                old_value, None,
571                "attempted to insert the same list twice, this is a bug"
572            );
573
574            (ptr.cast_const(), len)
575        }
576
577        /// Get a list back from a previous call to [`list_into_sys`](Self::list_into_sys).
578        ///
579        /// # Safety
580        /// - `ptr` must have been returned from a call to `list_into_sys` on `self`.
581        /// - `ptr` must not have been used in a call to this function before.
582        /// - `ptr` must not have been mutated since the call to `list_into_sys`.
583        /// - `ptr` must not be accessed after calling this function.
584        #[deny(unsafe_op_in_unsafe_fn)]
585        pub unsafe fn list_from_sys(&self, ptr: *const T) -> Box<[T]> {
586            let ptr: *mut T = ptr.cast_mut();
587            let len = self
588                .list_lengths
589                .lock()
590                .unwrap()
591                .remove(&ptr)
592                .expect("attempted to free list from wrong collection, this is a bug");
593            let len: usize = sys::conv::u32_to_usize(len);
594
595            // SAFETY: `ptr` was created in `list_into_sys` from a slice of length `len`.
596            // And has not been mutated since.
597            let slice = unsafe { std::slice::from_raw_parts_mut(ptr, len) };
598
599            // SAFETY: This is the first call to this function, and the list will not be accessed again after this function call.
600            unsafe { Box::from_raw(slice) }
601        }
602    }
603}
604
605#[deny(unsafe_op_in_unsafe_fn)]
606mod script_instance_info {
607    use std::any::type_name;
608    use std::ffi::c_void;
609
610    use crate::builtin::{StringName, Variant};
611    use crate::private::handle_panic;
612    use crate::sys;
613
614    use super::{ScriptInstance, ScriptInstanceData, SiMut};
615    use crate::meta::{MethodInfo, PropertyInfo};
616    use sys::conv::{bool_to_sys, SYS_FALSE, SYS_TRUE};
617    #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
618    use sys::conv::{ptr_list_from_sys, ptr_list_into_sys};
619
620    /// # Safety
621    ///
622    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
623    /// - `p_name` must be a valid [`StringName`] pointer.
624    /// - `p_value` must be a valid [`Variant`] pointer.
625    pub(super) unsafe extern "C" fn set_property_func<T: ScriptInstance>(
626        p_instance: sys::GDExtensionScriptInstanceDataPtr,
627        p_name: sys::GDExtensionConstStringNamePtr,
628        p_value: sys::GDExtensionConstVariantPtr,
629    ) -> sys::GDExtensionBool {
630        let (name, value);
631        // SAFETY: `p_name` and `p_value` are valid pointers to a `StringName` and `Variant`.
632        unsafe {
633            name = StringName::new_from_string_sys(p_name);
634            value = Variant::borrow_var_sys(p_value);
635        }
636        let ctx = || format!("error when calling {}::set", type_name::<T>());
637
638        let result = handle_panic(ctx, || {
639            // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
640            let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
641            let mut guard = instance.borrow_mut();
642
643            let instance_guard = SiMut::new(instance.cell_ref(), &mut guard, &instance.base);
644
645            ScriptInstance::set_property(instance_guard, name, value)
646        })
647        // Unwrapping to a default of false, to indicate that the assignment is not handled by the script.
648        .unwrap_or_default();
649
650        bool_to_sys(result)
651    }
652
653    /// # Safety
654    ///
655    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
656    /// - `p_name` must be a valid [`StringName`] pointer.
657    /// - It must be safe to move a `Variant` into `r_ret`.
658    pub(super) unsafe extern "C" fn get_property_func<T: ScriptInstance>(
659        p_instance: sys::GDExtensionScriptInstanceDataPtr,
660        p_name: sys::GDExtensionConstStringNamePtr,
661        r_ret: sys::GDExtensionVariantPtr,
662    ) -> sys::GDExtensionBool {
663        // SAFETY: `p_name` is a valid [`StringName`] pointer.
664        let name = unsafe { StringName::new_from_string_sys(p_name) };
665        let ctx = || format!("error when calling {}::get", type_name::<T>());
666
667        let return_value = handle_panic(ctx, || {
668            // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
669            unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) }
670                .borrow()
671                .get_property(name)
672        });
673
674        match return_value {
675            Ok(Some(variant)) => {
676                // SAFETY: It is safe to move a `Variant` into `r_ret`.
677                unsafe { variant.move_into_var_ptr(r_ret) };
678                SYS_TRUE
679            }
680            _ => SYS_FALSE,
681        }
682    }
683
684    /// # Safety
685    ///
686    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
687    /// - It must be safe to assign a `u32` to `r_count`.
688    pub(super) unsafe extern "C" fn get_property_list_func<T: ScriptInstance>(
689        p_instance: sys::GDExtensionScriptInstanceDataPtr,
690        r_count: *mut u32,
691    ) -> *const sys::GDExtensionPropertyInfo {
692        let ctx = || format!("error when calling {}::get_property_list", type_name::<T>());
693
694        // Encapsulate this unsafe block to avoid repeating the safety comment.
695        // SAFETY: This closure is only used in this function, and we may dereference `p_instance` to an immutable reference for the duration of
696        // this call.
697        let borrow_instance =
698            move || unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
699
700        let property_list = handle_panic(ctx, || {
701            let property_list = borrow_instance().borrow().get_property_list();
702
703            property_list
704                .into_iter()
705                .map(|prop| prop.into_owned_property_sys())
706                .collect::<Vec<_>>()
707        })
708        .unwrap_or_default();
709
710        #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
711        let (list_ptr, list_length) = borrow_instance()
712            .property_lists
713            .list_into_sys(property_list);
714
715        #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
716        let (list_ptr, list_length) = ptr_list_into_sys(property_list);
717
718        // SAFETY: It is safe to assign a `u32` to `r_count`.
719        unsafe {
720            *r_count = list_length;
721        }
722
723        list_ptr
724    }
725
726    /// # Safety
727    ///
728    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
729    /// - `r_count` is expected to be a valid pointer to an u32.
730    pub(super) unsafe extern "C" fn get_method_list_func<T: ScriptInstance>(
731        p_instance: sys::GDExtensionScriptInstanceDataPtr,
732        r_count: *mut u32,
733    ) -> *const sys::GDExtensionMethodInfo {
734        let ctx = || format!("error when calling {}::get_method_list", type_name::<T>());
735
736        // Encapsulate this unsafe block to avoid repeating the safety comment.
737        // SAFETY: This closure is only used in this function, and we may dereference `p_instance` to an immutable reference for the duration of
738        // this call.
739        let borrow_instance =
740            move || unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
741
742        let method_list = handle_panic(ctx, || {
743            let method_list = borrow_instance().borrow().get_method_list();
744
745            method_list
746                .into_iter()
747                .map(|method| method.into_owned_method_sys())
748                .collect()
749        })
750        .unwrap_or_default();
751
752        #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
753        let (return_pointer, list_length) =
754            borrow_instance().method_lists.list_into_sys(method_list);
755        #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
756        let (return_pointer, list_length) = ptr_list_into_sys(method_list);
757
758        unsafe {
759            *r_count = list_length;
760        }
761
762        return_pointer
763    }
764
765    /// Provides the same functionality as the function below, but for Godot 4.2 and lower.
766    ///
767    /// # Safety
768    ///
769    /// See latest version below.
770    #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
771    pub(super) unsafe extern "C" fn free_property_list_func<T: ScriptInstance>(
772        p_instance: sys::GDExtensionScriptInstanceDataPtr,
773        p_prop_info: *const sys::GDExtensionPropertyInfo,
774    ) {
775        // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
776        let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
777
778        // SAFETY: `p_prop_info` was returned from a call to `list_into_sys`, and has not been mutated since. This is also the first call
779        // to `list_from_sys` with this pointer.
780        let property_infos = unsafe { instance.property_lists.list_from_sys(p_prop_info) };
781
782        for info in property_infos.iter() {
783            // SAFETY: `info` was returned from a call to `into_owned_property_sys` and this is the first and only time this function is called
784            // on it.
785            unsafe { PropertyInfo::free_owned_property_sys(*info) };
786        }
787    }
788
789    /// # Safety
790    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
791    /// - `p_prop_info` must have been returned from a call to [`get_property_list_func`] called with the same `p_instance` pointer.
792    /// - `p_prop_info` must not have been mutated since the call to `get_property_list_func`.
793    #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
794    pub(super) unsafe extern "C" fn free_property_list_func(
795        _p_instance: sys::GDExtensionScriptInstanceDataPtr,
796        p_prop_info: *const sys::GDExtensionPropertyInfo,
797        p_len: u32,
798    ) {
799        // SAFETY: `p_prop_info` was returned from a call to `list_into_sys`, and has not been mutated since. This is also the first call
800        // to `list_from_sys` with this pointer.
801        let property_infos = unsafe { ptr_list_from_sys(p_prop_info, p_len) };
802
803        for info in property_infos.iter() {
804            // SAFETY: `info` was returned from a call to `into_owned_property_sys` and this is the first and only time this function is called
805            // on it.
806            unsafe { PropertyInfo::free_owned_property_sys(*info) };
807        }
808    }
809
810    /// # Safety
811    ///
812    /// - `p_self` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
813    /// - `p_method` must be a valid [`StringName`] pointer.
814    /// - `p_args` has to point to a list of Variant pointers of length `p_argument_count`.
815    /// - All the variant pointers in `p_args`, as well the `p_args` pointer itself must be dereferenceable to an immutable reference for the
816    ///   duration of this call.
817    /// - It must be safe to move a [`Variant`] into `r_return`.
818    /// - `r_error` must point to an initialized [`sys::GDExtensionCallError`] which can be written to.
819    pub(super) unsafe extern "C" fn call_func<T: ScriptInstance>(
820        p_self: sys::GDExtensionScriptInstanceDataPtr,
821        p_method: sys::GDExtensionConstStringNamePtr,
822        p_args: *const sys::GDExtensionConstVariantPtr,
823        p_argument_count: sys::GDExtensionInt,
824        r_return: sys::GDExtensionVariantPtr,
825        r_error: *mut sys::GDExtensionCallError,
826    ) {
827        // SAFETY: `p_method` is a valid [`StringName`] pointer.
828        let method = unsafe { StringName::new_from_string_sys(p_method) };
829        // SAFETY: `p_args` is a valid array of length `p_argument_count`
830        let args = unsafe {
831            Variant::borrow_ref_slice(
832                p_args,
833                p_argument_count
834                    .try_into()
835                    .expect("argument count should be a valid `u32`"),
836            )
837        };
838        let ctx = || format!("error when calling {}::call", type_name::<T>());
839
840        let result = handle_panic(ctx, || {
841            // SAFETY: `p_self` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
842            let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_self) };
843            let mut guard = instance.borrow_mut();
844
845            let instance_guard = SiMut::new(instance.cell_ref(), &mut guard, &instance.base);
846
847            ScriptInstance::call(instance_guard, method.clone(), args)
848        });
849
850        let error = match result {
851            Ok(Ok(variant)) => {
852                // SAFETY: It is safe to move a `Variant` into `r_return`.
853                unsafe { variant.move_into_var_ptr(r_return) };
854                sys::GDEXTENSION_CALL_OK
855            }
856
857            Ok(Err(err)) => err,
858
859            Err(_) => sys::GDEXTENSION_CALL_ERROR_INVALID_METHOD,
860        };
861
862        // SAFETY: `r_error` is an initialized pointer which we can write to.
863        unsafe { (*r_error).error = error };
864    }
865
866    /// Ownership of the returned object is not transferred to the caller. The caller is therefore responsible for incrementing the reference
867    /// count.
868    ///
869    /// # Safety
870    ///
871    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
872    pub(super) unsafe extern "C" fn get_script_func<T: ScriptInstance>(
873        p_instance: sys::GDExtensionScriptInstanceDataPtr,
874    ) -> sys::GDExtensionObjectPtr {
875        let ctx = || format!("error when calling {}::get_script", type_name::<T>());
876
877        let script = handle_panic(ctx, || {
878            // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
879            unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) }
880                .borrow()
881                .get_script()
882                .clone()
883        });
884
885        match script {
886            Ok(script) => script.obj_sys(),
887            Err(_) => std::ptr::null_mut(),
888        }
889    }
890
891    /// # Safety
892    ///
893    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
894    pub(super) unsafe extern "C" fn is_placeholder_func<T: ScriptInstance>(
895        p_instance: sys::GDExtensionScriptInstanceDataPtr,
896    ) -> sys::GDExtensionBool {
897        let ctx = || format!("error when calling {}::is_placeholder", type_name::<T>());
898
899        let is_placeholder = handle_panic(ctx, || {
900            // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
901            unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) }
902                .borrow()
903                .is_placeholder()
904        })
905        .unwrap_or_default();
906
907        bool_to_sys(is_placeholder)
908    }
909
910    /// # Safety
911    ///
912    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
913    /// - `p_method` has to point to a valid `StringName`.
914    pub(super) unsafe extern "C" fn has_method_func<T: ScriptInstance>(
915        p_instance: sys::GDExtensionScriptInstanceDataPtr,
916        p_method: sys::GDExtensionConstStringNamePtr,
917    ) -> sys::GDExtensionBool {
918        // SAFETY: `p_method` is a valid [`StringName`] pointer.
919        let method = unsafe { StringName::new_from_string_sys(p_method) };
920        let ctx = || format!("error when calling {}::has_method", type_name::<T>());
921
922        let has_method = handle_panic(ctx, || {
923            // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
924            unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) }
925                .borrow()
926                .has_method(method)
927        })
928        .unwrap_or_default();
929
930        bool_to_sys(has_method)
931    }
932
933    /// Provides the same functionality as the function below, but for Godot 4.2 and lower.
934    ///
935    /// # Safety
936    ///
937    /// See latest version below.
938    #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
939    pub(super) unsafe extern "C" fn free_method_list_func<T: ScriptInstance>(
940        p_instance: sys::GDExtensionScriptInstanceDataPtr,
941        p_method_info: *const sys::GDExtensionMethodInfo,
942    ) {
943        // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
944        let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
945
946        // SAFETY: `p_method_info` was returned from a call to `list_into_sys`, and has not been mutated since. This is also the first call
947        // to `list_from_sys` with this pointer.
948        let method_infos = unsafe { instance.method_lists.list_from_sys(p_method_info) };
949
950        for info in method_infos.iter() {
951            // SAFETY: `info` was returned from a call to `into_owned_method_sys`, and this is the first and only time we call this method on
952            // it.
953            unsafe { MethodInfo::free_owned_method_sys(*info) };
954        }
955    }
956
957    /// # Safety
958    ///
959    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
960    /// - `p_method_info` must have been returned from a call to [`get_method_list_func`] called with the same `p_instance` pointer.
961    /// - `p_method_info` must not have been mutated since the call to `get_method_list_func`.
962    #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
963    pub(super) unsafe extern "C" fn free_method_list_func(
964        _p_instance: sys::GDExtensionScriptInstanceDataPtr,
965        p_method_info: *const sys::GDExtensionMethodInfo,
966        p_len: u32,
967    ) {
968        // SAFETY: `p_method_info` was returned from a call to `list_into_sys`, and has not been mutated since. This is also the first call
969        // to `list_from_sys` with this pointer.
970        let method_infos = unsafe { ptr_list_from_sys(p_method_info, p_len) };
971
972        for info in method_infos.iter() {
973            // SAFETY: `info` was returned from a call to `into_owned_method_sys`, and this is the first and only time we call this method on
974            // it.
975            unsafe { MethodInfo::free_owned_method_sys(*info) };
976        }
977    }
978
979    /// # Safety
980    ///
981    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
982    /// - `p_name` must be a valid [`StringName`] pointer.
983    /// - `r_is_valid` must be assignable.
984    pub(super) unsafe extern "C" fn get_property_type_func<T: ScriptInstance>(
985        p_instance: sys::GDExtensionScriptInstanceDataPtr,
986        p_name: sys::GDExtensionConstStringNamePtr,
987        r_is_valid: *mut sys::GDExtensionBool,
988    ) -> sys::GDExtensionVariantType {
989        let ctx = || {
990            format!(
991                "error while calling {}::get_property_type",
992                type_name::<T>()
993            )
994        };
995        // SAFETY: `p_name` is a valid [`StringName`] pointer.
996        let name = unsafe { StringName::new_from_string_sys(p_name) };
997
998        let result = handle_panic(ctx, || {
999            // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
1000            unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) }
1001                .borrow()
1002                .get_property_type(name.clone())
1003        });
1004
1005        let (is_valid, result) = if let Ok(result) = result {
1006            (SYS_TRUE, result.sys())
1007        } else {
1008            (SYS_FALSE, sys::GDEXTENSION_VARIANT_TYPE_NIL)
1009        };
1010
1011        // SAFETY: `r_is_valid` is assignable.
1012        unsafe { *r_is_valid = is_valid };
1013        result
1014    }
1015
1016    /// # Safety
1017    ///
1018    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
1019    /// - `r_is_valid` must be assignable.
1020    /// - It must be safe to move a [`GString`](crate::builtin::GString) into `r_str`.
1021    pub(super) unsafe extern "C" fn to_string_func<T: ScriptInstance>(
1022        p_instance: sys::GDExtensionScriptInstanceDataPtr,
1023        r_is_valid: *mut sys::GDExtensionBool,
1024        r_str: sys::GDExtensionStringPtr,
1025    ) {
1026        let ctx = || format!("error when calling {}::to_string", type_name::<T>());
1027
1028        let string = handle_panic(ctx, || {
1029            // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
1030            unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) }
1031                .borrow()
1032                .to_string()
1033        })
1034        .ok();
1035
1036        let Some(string) = string else {
1037            return;
1038        };
1039
1040        // SAFETY: `r_is_valid` is assignable.
1041        unsafe { *r_is_valid = SYS_TRUE };
1042        // SAFETY: It is safe to move a `GString` into `r_str`.
1043        unsafe { string.move_into_string_ptr(r_str) };
1044    }
1045
1046    /// # Safety
1047    ///
1048    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
1049    ///
1050    /// If `property_state_add` is non-null, then:
1051    /// - It is safe to call `property_state_add` using the provided `userdata`.
1052    /// - `property_state_add` must take ownership of the `StringName` and `Variant` it is called with.
1053    pub(super) unsafe extern "C" fn get_property_state_func<T: ScriptInstance>(
1054        p_instance: sys::GDExtensionScriptInstanceDataPtr,
1055        property_state_add: sys::GDExtensionScriptInstancePropertyStateAdd,
1056        userdata: *mut c_void,
1057    ) {
1058        let ctx = || {
1059            format!(
1060                "error when calling {}::get_property_state",
1061                type_name::<T>()
1062            )
1063        };
1064
1065        let property_states = handle_panic(ctx, || {
1066            // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
1067            unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) }
1068                .borrow()
1069                .get_property_state()
1070        })
1071        .unwrap_or_default();
1072
1073        let Some(property_state_add) = property_state_add else {
1074            return;
1075        };
1076
1077        for (name, value) in property_states {
1078            // SAFETY: `property_state_add` is non-null, therefore we can call the function with the provided `userdata`.
1079            // Additionally `property_state_add` takes ownership of `name` and `value`.
1080            unsafe {
1081                property_state_add(
1082                    name.into_owned_string_sys(),
1083                    value.into_owned_var_sys(),
1084                    userdata,
1085                )
1086            }
1087        }
1088    }
1089
1090    /// Ownership of the returned object is not transferred to the caller. The caller must therefore ensure it's not freed when used.
1091    ///
1092    /// # Safety
1093    ///
1094    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
1095    pub(super) unsafe extern "C" fn get_language_func<T: ScriptInstance>(
1096        p_instance: sys::GDExtensionScriptInstanceDataPtr,
1097    ) -> sys::GDExtensionScriptLanguagePtr {
1098        let ctx = || format!("error when calling {}::get_language", type_name::<T>());
1099
1100        let language = handle_panic(ctx, || {
1101            // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
1102            unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) }
1103                .borrow()
1104                .get_language()
1105        });
1106
1107        if let Ok(language) = language {
1108            language.obj_sys().cast()
1109        } else {
1110            std::ptr::null_mut()
1111        }
1112    }
1113
1114    /// # Safety
1115    ///
1116    /// - `p_instance` must fulfill the safety preconditions of [`Box::from_raw`] for `Box<ScriptInstanceData<T>>`.
1117    pub(super) unsafe extern "C" fn free_func<T: ScriptInstance>(
1118        p_instance: sys::GDExtensionScriptInstanceDataPtr,
1119    ) {
1120        unsafe { drop(Box::from_raw(p_instance.cast::<ScriptInstanceData<T>>())) }
1121    }
1122
1123    /// # Safety
1124    ///
1125    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
1126    pub(super) unsafe extern "C" fn refcount_decremented_func<T: ScriptInstance>(
1127        p_instance: sys::GDExtensionScriptInstanceDataPtr,
1128    ) -> sys::GDExtensionBool {
1129        let ctx = || {
1130            format!(
1131                "error when calling {}::refcount_decremented",
1132                type_name::<T>()
1133            )
1134        };
1135
1136        let result = handle_panic(ctx, || {
1137            // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
1138            unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) }
1139                .borrow()
1140                .on_refcount_decremented()
1141        })
1142        .unwrap_or(true);
1143
1144        bool_to_sys(result)
1145    }
1146
1147    /// # Safety
1148    ///
1149    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
1150    pub(super) unsafe extern "C" fn refcount_incremented_func<T: ScriptInstance>(
1151        p_instance: sys::GDExtensionScriptInstanceDataPtr,
1152    ) {
1153        let ctx = || {
1154            format!(
1155                "error when calling {}::refcount_incremented",
1156                type_name::<T>()
1157            )
1158        };
1159
1160        handle_panic(ctx, || {
1161            // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
1162            unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) }
1163                .borrow()
1164                .on_refcount_incremented();
1165        })
1166        .unwrap_or_default();
1167    }
1168
1169    /// # Safety
1170    ///
1171    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
1172    /// - `p_name` must be a valid [`StringName`] pointer.
1173    /// - It must be safe to move a `Variant` into `r_ret`.
1174    pub(super) unsafe extern "C" fn get_fallback_func<T: ScriptInstance>(
1175        p_instance: sys::GDExtensionScriptInstanceDataPtr,
1176        p_name: sys::GDExtensionConstStringNamePtr,
1177        r_ret: sys::GDExtensionVariantPtr,
1178    ) -> sys::GDExtensionBool {
1179        // SAFETY: `p_name` is a valid `StringName` pointer.
1180        let name = unsafe { StringName::new_from_string_sys(p_name) };
1181
1182        let ctx = || {
1183            format!(
1184                "error when calling {}::property_get_fallback",
1185                type_name::<T>()
1186            )
1187        };
1188
1189        let return_value = handle_panic(ctx, || {
1190            // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
1191            unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) }
1192                .borrow()
1193                .property_get_fallback(name)
1194        });
1195
1196        match return_value {
1197            Ok(Some(variant)) => {
1198                // SAFETY: It is safe to move a `Variant` into `r_ret`.
1199                unsafe { variant.move_into_var_ptr(r_ret) };
1200                SYS_TRUE
1201            }
1202            _ => SYS_FALSE,
1203        }
1204    }
1205
1206    /// # Safety
1207    ///
1208    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
1209    /// - `p_name` must be a valid [`StringName`] pointer.
1210    /// - `p_value` must be a valid [`Variant`] pointer.
1211    pub(super) unsafe extern "C" fn set_fallback_func<T: ScriptInstance>(
1212        p_instance: sys::GDExtensionScriptInstanceDataPtr,
1213        p_name: sys::GDExtensionConstStringNamePtr,
1214        p_value: sys::GDExtensionConstVariantPtr,
1215    ) -> sys::GDExtensionBool {
1216        let (name, value);
1217        // SAFETY: `p_name` and `p_value` are valid `StringName` and `Variant` pointers respectively.
1218        unsafe {
1219            name = StringName::new_from_string_sys(p_name);
1220            value = Variant::borrow_var_sys(p_value);
1221        };
1222
1223        let ctx = || {
1224            format!(
1225                "error when calling {}::property_set_fallback",
1226                type_name::<T>()
1227            )
1228        };
1229
1230        let result = handle_panic(ctx, || {
1231            // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
1232            let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
1233            let mut guard = instance.borrow_mut();
1234
1235            let instance_guard = SiMut::new(instance.cell_ref(), &mut guard, &instance.base);
1236            ScriptInstance::property_set_fallback(instance_guard, name, value)
1237        })
1238        .unwrap_or_default();
1239
1240        bool_to_sys(result)
1241    }
1242
1243    /// # Safety
1244    ///
1245    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
1246    /// - `p_method` has to point to a valid [`StringName`].
1247    /// - `p_value` must be a valid [`sys::GDExtensionBool`] pointer.
1248    #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
1249    pub(super) unsafe extern "C" fn get_method_argument_count_func<T: ScriptInstance>(
1250        p_instance: sys::GDExtensionScriptInstanceDataPtr,
1251        p_method: sys::GDExtensionConstStringNamePtr,
1252        r_is_valid: *mut sys::GDExtensionBool,
1253    ) -> sys::GDExtensionInt {
1254        // SAFETY: `p_method` is a valid [`StringName`] pointer.
1255        let method = unsafe { StringName::new_from_string_sys(p_method) };
1256        let ctx = || {
1257            format!(
1258                "error when calling {}::get_method_argument_count_func",
1259                type_name::<T>()
1260            )
1261        };
1262
1263        let method_argument_count = handle_panic(ctx, || {
1264            // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
1265            unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) }
1266                // Can panic if the GdCell is currently mutably bound.
1267                .borrow()
1268                // This is user code and could cause a panic.
1269                .get_method_argument_count(method)
1270        })
1271        // In case of a panic, handle_panic will print an error message. We will recover from the panic by falling back to the default value None.
1272        .unwrap_or_default();
1273
1274        let (result, is_valid) = match method_argument_count {
1275            Some(count) => (count, SYS_TRUE),
1276            None => (0, SYS_FALSE),
1277        };
1278
1279        // SAFETY: `r_is_valid` is assignable.
1280        unsafe { *r_is_valid = is_valid };
1281
1282        result.into()
1283    }
1284}