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