Skip to main content

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::RawPtr;
26use crate::meta::error::CallErrorType;
27use crate::obj::{Base, Gd, GodotClass};
28use crate::registry::info::{MethodInfo, PropertyInfo};
29use crate::sys;
30
31// ----------------------------------------------------------------------------------------------------------------------------------------------
32// Public re-exports.
33
34mod reexport_pub {
35    pub use crate::classes::IScriptExtension;
36    pub use crate::obj::Inherits;
37}
38pub use reexport_pub::*;
39
40// Re-export guards.
41pub use crate::obj::guards::{ScriptBaseMut, ScriptBaseRef};
42
43/// Implement custom scripts that can be attached to objects in Godot.
44///
45/// To use script instances, implement this trait for your own type.
46///
47/// You can use the [`create_script_instance()`] function to create a low-level pointer to your script instance. This pointer should then be
48/// returned from [`IScriptExtension::instance_create_rawptr()`](crate::classes::IScriptExtension::instance_create_rawptr).
49///
50/// # Example
51///
52/// ```no_run
53/// # // Trick 17 to avoid listing all the methods. Needs also a method.
54/// # mod godot {
55/// #     pub use ::godot::*;
56/// #     pub mod extras { pub trait ScriptInstance {} pub trait IScriptExtension {} }
57/// # }
58/// # fn create_script_instance(_: MyInstance) -> *mut std::ffi::c_void { std::ptr::null_mut() }
59/// use godot::prelude::*;
60/// use godot::classes::{Script, ScriptExtension};
61/// use godot::extras::{IScriptExtension, ScriptInstance};
62///
63/// // 1) Define the script.
64/// // This needs #[class(tool)] since the script extension runs in the editor.
65/// #[derive(GodotClass)]
66/// #[class(init, base=ScriptExtension, tool)]
67/// struct MyScript {
68///    base: Base<ScriptExtension>,
69///    // ... other fields
70/// }
71///
72/// // 2) Define the script _instance_, and implement the trait for it.
73/// struct MyInstance;
74/// impl MyInstance {
75///     fn from_gd(script: Gd<Script>) -> Self {
76///         Self { /* ... */ }
77///     }
78/// }
79///
80/// impl ScriptInstance for MyInstance {
81///     // Implement all the methods...
82/// }
83///
84/// // 3) Implement the script's virtual interface to wire up 1) and 2).
85/// #[godot_api]
86/// impl IScriptExtension for MyScript {
87///     // Implement all the methods...
88/// }
89/// ```
90pub trait ScriptInstance: Sized {
91    type Base: GodotClass;
92
93    /// Name of the new class the script implements.
94    fn class_name(&self) -> GString;
95
96    /// Property setter for Godot's virtual dispatch system.
97    ///
98    /// The engine will call this function when it wants to change a property on the script.
99    fn set_property(this: SiMut<Self>, name: StringName, value: &Variant) -> bool;
100
101    /// Property getter for Godot's virtual dispatch system.
102    ///
103    /// The engine will call this function when it wants to read a property on the script.
104    fn get_property(&self, name: StringName) -> Option<Variant>;
105
106    /// A list of all the properties a script exposes to the engine.
107    fn get_property_list(&self) -> Vec<PropertyInfo>;
108
109    /// A list of all the methods a script exposes to the engine.
110    fn get_method_list(&self) -> Vec<MethodInfo>;
111
112    /// Method invoker for Godot's virtual dispatch system. The engine will call this function when it wants to call a method on the script.
113    ///
114    /// All method calls are taking a mutable reference of the script instance, as the engine does not differentiate between immutable and
115    /// mutable method calls like rust.
116    ///
117    /// 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.
118    fn call(
119        this: SiMut<Self>,
120        method: StringName,
121        args: &[&Variant],
122    ) -> Result<Variant, CallErrorType>;
123
124    /// Identifies the script instance as a placeholder, routing property writes to a fallback if applicable.
125    ///
126    /// If this function and [IScriptExtension::is_placeholder_fallback_enabled] return true, Godot will call [`Self::property_set_fallback`]
127    /// instead of [`Self::set_property`].
128    fn is_placeholder(&self) -> bool;
129
130    /// Validation function for the engine to verify if the script exposes a certain method.
131    fn has_method(&self, method: StringName) -> bool;
132
133    /// Lets the engine get a reference to the script this instance was created for.
134    ///
135    /// This function has to return a reference, because scripts are reference-counted in Godot, and it must be guaranteed that the object is
136    /// not freed before the engine increased the reference count. (Every time a ref-counted `Gd<T>` is dropped, the reference count is
137    /// decremented.)
138    fn get_script(&self) -> &Gd<Script>;
139
140    /// Lets the engine fetch the type of a particular property.
141    fn get_property_type(&self, name: StringName) -> VariantType;
142
143    /// String representation of the script instance.
144    fn to_string(&self) -> GString;
145
146    /// A dump of all property names and values that are exposed to the engine.
147    fn get_property_state(&self) -> Vec<(StringName, Variant)>;
148
149    /// Lets the engine get a reference to the [`ScriptLanguage`] this instance belongs to.
150    fn get_language(&self) -> Gd<ScriptLanguage>;
151
152    /// Callback from the engine when the reference count of the base object has been decreased. When this method returns `true` the engine will
153    /// not free the object the script is attached to.
154    fn on_refcount_decremented(&self) -> bool;
155
156    /// Callback from the engine when the reference count of the base object has been increased.
157    fn on_refcount_incremented(&self);
158
159    /// The engine may call this function if it failed to get a property value via [`ScriptInstance::get_property`] or the native type's getter.
160    fn property_get_fallback(&self, name: StringName) -> Option<Variant>;
161
162    /// The engine may call this function if [`IScriptExtension::is_placeholder_fallback_enabled`] is enabled.
163    fn property_set_fallback(this: SiMut<Self>, name: StringName, value: &Variant) -> bool;
164
165    /// This function will be called to handle calls to [`Object::get_method_argument_count`](crate::classes::Object::get_method_argument_count)
166    /// and `Callable::get_argument_count`.
167    ///
168    /// If `None` is returned the public methods will return `0`.
169    #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
170    fn get_method_argument_count(&self, _method: StringName) -> Option<u32>;
171}
172
173#[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
174type ScriptInstanceInfo = sys::GDExtensionScriptInstanceInfo2;
175#[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
176type ScriptInstanceInfo = sys::GDExtensionScriptInstanceInfo3;
177
178struct ScriptInstanceData<T: ScriptInstance> {
179    inner: GdCell<T>,
180    script_instance_ptr: *mut ScriptInstanceInfo,
181    #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
182    property_lists: BoundedPtrList<sys::GDExtensionPropertyInfo>,
183    #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
184    method_lists: BoundedPtrList<sys::GDExtensionMethodInfo>,
185    base: Base<T::Base>,
186}
187
188// `RefUnwindSafe` requires that a `&Self` cannot expose logically inconsistent state after a caught panic. The interior mutability here
189// is panic-safe by construction: `GdCell<T>` uses RAII borrow guards (a panic during a borrow drops the guard, returning the cell to a
190// clean unborrowed state), and `Base<T::Base>` contains `Rc<Cell<InitState>>` whose `set` is an uninterruptible value replacement.
191// Therefore no inconsistent state is observable via a `&ScriptInstanceData<T>` after an unwind.
192impl<T: ScriptInstance> std::panic::RefUnwindSafe for ScriptInstanceData<T> {}
193
194impl<T: ScriptInstance> ScriptInstanceData<T> {
195    ///  Convert a `ScriptInstanceData` sys pointer to a reference with unbounded lifetime.
196    ///
197    /// # Safety
198    ///
199    /// `ptr` must point to a live `ScriptInstanceData<T>` for the duration of `'a`.
200    #[allow(unsafe_op_in_unsafe_fn)] // Safety preconditions forwarded 1:1.
201    unsafe fn borrow_script_sys<'a>(ptr: sys::GDExtensionScriptInstanceDataPtr) -> &'a Self {
202        &*(ptr.cast::<ScriptInstanceData<T>>())
203    }
204
205    fn borrow(&self) -> RefGuard<'_, T> {
206        self.inner
207            .borrow()
208            .unwrap_or_else(|err| Self::borrow_panic(err))
209    }
210
211    fn borrow_mut(&self) -> MutGuard<'_, T> {
212        self.inner
213            .borrow_mut()
214            .unwrap_or_else(|err| Self::borrow_panic(err))
215    }
216
217    fn cell_ref(&self) -> &GdCell<T> {
218        &self.inner
219    }
220
221    fn borrow_panic(err: Box<dyn std::error::Error>) -> ! {
222        panic!(
223            "\
224                ScriptInstance borrow failed, already bound; T = {}.\n  \
225                Make sure to use `SiMut::base_mut()` when possible.\n  \
226                Details: {err}.\
227            ",
228            std::any::type_name::<T>(),
229        )
230    }
231}
232
233impl<T: ScriptInstance> Drop for ScriptInstanceData<T> {
234    fn drop(&mut self) {
235        // SAFETY: The ownership of ScriptInstanceData is transferred to Godot after its creation. The engine then calls
236        // script_instance_info::free_func when it frees its own GDExtensionScriptInstance and subsequently wants to drop the ScriptInstanceData.
237        // After the data has been dropped, the instance info is no longer being used, but never freed. It is therefore safe to drop the
238        // instance info at the same time.
239        let instance = unsafe { Box::from_raw(self.script_instance_ptr) };
240
241        drop(instance);
242    }
243}
244
245/// Creates a raw pointer to a Godot script instance, from a Rust [`ScriptInstance`] object.
246///
247/// See [`ScriptInstance`] for usage. Discarding the resulting value will result in a memory leak.
248///
249/// The exact GDExtension type of the pointer is `sys::GDExtensionScriptInstancePtr`, but you can treat it like an opaque pointer.
250///
251/// # Safety
252/// The caller must ensure that `for_object` is not freed before passing the returned pointer back to Godot.
253#[must_use]
254pub unsafe fn create_script_instance<T: ScriptInstance>(
255    rust_instance: T,
256    for_object: Gd<T::Base>,
257) -> RawPtr<*mut c_void> {
258    // Field grouping matches C header.
259    let gd_instance = ScriptInstanceInfo {
260        set_func: Some(script_instance_info::set_property_func::<T>),
261        get_func: Some(script_instance_info::get_property_func::<T>),
262        get_property_list_func: Some(script_instance_info::get_property_list_func::<T>),
263        #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
264        free_property_list_func: Some(script_instance_info::free_property_list_func::<T>),
265        #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
266        free_property_list_func: Some(script_instance_info::free_property_list_func),
267
268        get_class_category_func: None, // not yet implemented.
269
270        property_can_revert_func: None, // unimplemented until needed.
271        property_get_revert_func: None, // unimplemented until needed.
272
273        get_owner_func: None,
274        get_property_state_func: Some(script_instance_info::get_property_state_func::<T>),
275
276        get_method_list_func: Some(script_instance_info::get_method_list_func::<T>),
277        #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
278        free_method_list_func: Some(script_instance_info::free_method_list_func::<T>),
279        #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
280        free_method_list_func: Some(script_instance_info::free_method_list_func),
281        get_property_type_func: Some(script_instance_info::get_property_type_func::<T>),
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    let instance_extension: sys::GDExtensionScriptInstancePtr = unsafe {
332        #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(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        )
342    };
343
344    // SAFETY: object validity guaranteed per this function's safety precondition.
345    unsafe { RawPtr::new(instance_extension.cast::<c_void>()) }
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`].
354pub fn script_instance_exists<O, S>(object: &Gd<O>, script: &Gd<S>) -> bool
355where
356    O: Inherits<Object>,
357    S: Inherits<Script> + IScriptExtension + super::Bounds<Declarer = super::bounds::DeclUser>,
358{
359    let Some(object_script) = object.upcast_ref().get_script() else {
360        return false;
361    };
362
363    if object_script.instance_id() != script.instance_id() {
364        return false;
365    }
366
367    let Some(language) = script.bind().get_language() else {
368        return false;
369    };
370
371    let get_instance_fn = sys::interface_fn!(object_get_script_instance);
372
373    // SAFETY: Object and language are alive and their sys pointers are valid.
374    let instance = unsafe { get_instance_fn(object.obj_sys(), language.obj_sys()) };
375
376    !instance.is_null()
377}
378
379/// Mutable/exclusive reference guard for a `T` where `T` implements [`ScriptInstance`].
380///
381/// 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.
382/// For details see [`SiMut::base_mut()`].
383pub struct SiMut<'a, T: ScriptInstance> {
384    mut_ref: &'a mut T,
385    cell: &'a GdCell<T>,
386    base_ref: &'a Base<T::Base>,
387}
388
389impl<'a, T: ScriptInstance> SiMut<'a, T> {
390    fn new(
391        cell: &'a GdCell<T>,
392        cell_guard: &'a mut MutGuard<T>,
393        base_ref: &'a Base<T::Base>,
394    ) -> Self {
395        let mut_ref = cell_guard.deref_mut();
396
397        Self {
398            mut_ref,
399            cell,
400            base_ref,
401        }
402    }
403
404    /// Returns a shared reference suitable for calling engine methods on this object.
405    ///
406    /// Holding a shared guard prevents other code paths from obtaining a _mutable_ reference to `self`, as such it is recommended to drop the
407    /// guard as soon as you no longer need it.
408    ///
409    /// ```no_run
410    /// # use godot::prelude::*;
411    /// # use godot::classes::{ScriptLanguage, Script};
412    /// # use godot::obj::script::{ScriptInstance, SiMut};
413    /// # use godot::register::info::{MethodInfo, PropertyInfo};
414    /// # use godot::meta::error::CallErrorType;
415    /// # use godot::sys;
416    ///
417    /// struct ExampleScriptInstance;
418    ///
419    /// impl ScriptInstance for ExampleScriptInstance {
420    ///     type Base = Node;
421    ///
422    ///     fn call(
423    ///         this: SiMut<Self>,
424    ///         method: StringName,
425    ///         args: &[&Variant],
426    ///     ) -> Result<Variant, CallErrorType>{
427    ///         let name = this.base().get_name();
428    ///         godot_print!("name is {name}");
429    ///         // However, we cannot call methods that require `&mut Base`, such as:
430    ///         // this.base().add_child(&node);
431    ///         Ok(Variant::nil())
432    ///     }
433    ///     # fn class_name(&self) -> GString { todo!() }
434    ///     # fn set_property(_: SiMut<'_, Self>, _: StringName, _: &Variant) -> bool { todo!() }
435    ///     # fn get_property(&self, _: StringName) -> Option<Variant> { todo!() }
436    ///     # fn get_property_list(&self) -> Vec<PropertyInfo> { todo!() }
437    ///     # fn get_method_list(&self) -> Vec<MethodInfo> { todo!() }
438    ///     # fn is_placeholder(&self) -> bool { todo!() }
439    ///     # fn has_method(&self, _: StringName) -> bool { todo!() }
440    ///     # fn get_script(&self) -> &Gd<Script> { todo!() }
441    ///     # fn get_property_type(&self, _: StringName) -> VariantType { todo!() }
442    ///     # fn to_string(&self) -> GString { todo!() }
443    ///     # fn get_property_state(&self) -> Vec<(StringName, Variant)> { todo!() }
444    ///     # fn get_language(&self) -> Gd<ScriptLanguage> { todo!() }
445    ///     # fn on_refcount_decremented(&self) -> bool { todo!() }
446    ///     # fn on_refcount_incremented(&self) { todo!() }
447    ///     # fn property_get_fallback(&self, _: StringName) -> Option<Variant> { todo!() }
448    ///     # fn property_set_fallback(_: SiMut<'_, Self>, _: StringName, _: &Variant) -> bool { todo!() }
449    ///     # fn get_method_argument_count(&self, _: StringName) -> Option<u32> { todo!() }
450    /// }
451    /// ```
452    pub fn base(&self) -> ScriptBaseRef<'_, T> {
453        let passive_gd = self.base_ref.to_script_passive();
454        ScriptBaseRef::new(passive_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::register::info::{MethodInfo, PropertyInfo};
472    /// # use godot::meta::error::CallErrorType;
473    /// # use godot::sys;
474    ///
475    /// struct ExampleScriptInstance;
476    ///
477    /// impl ScriptInstance for ExampleScriptInstance {
478    ///     type Base = Object;
479    ///
480    ///     fn call(
481    ///         mut this: SiMut<Self>,
482    ///         method: StringName,
483    ///         args: &[&Variant],
484    ///     ) -> Result<Variant, CallErrorType> {
485    ///         // Check whether method is available on this script
486    ///         if method == StringName::from("script_method") {
487    ///             godot_print!("script_method called!");
488    ///             return Ok(true.to_variant());
489    ///         }
490    ///
491    ///         let node = Node::new_alloc();
492    ///
493    ///         // We can call back into `self` through Godot:
494    ///         this.base_mut().call("script_method", &[]);
495    ///
496    ///         Ok(Variant::nil())
497    ///     }
498    ///     # fn class_name(&self) -> GString { todo!() }
499    ///     # fn set_property(_: SiMut<'_, Self>, _: StringName, _: &Variant) -> bool { todo!() }
500    ///     # fn get_property(&self, _: StringName) -> Option<Variant> { todo!() }
501    ///     # fn get_property_list(&self) -> Vec<PropertyInfo> { todo!() }
502    ///     # fn get_method_list(&self) -> Vec<MethodInfo> { todo!() }
503    ///     # fn is_placeholder(&self) -> bool { todo!() }
504    ///     # fn has_method(&self, _: StringName) -> bool { todo!() }
505    ///     # fn get_script(&self) -> &Gd<Script> { todo!() }
506    ///     # fn get_property_type(&self, _: StringName) -> VariantType { todo!() }
507    ///     # fn to_string(&self) -> GString { todo!() }
508    ///     # fn get_property_state(&self) -> Vec<(StringName, Variant)> { todo!() }
509    ///     # fn get_language(&self) -> Gd<ScriptLanguage> { todo!() }
510    ///     # fn on_refcount_decremented(&self) -> bool { todo!() }
511    ///     # fn on_refcount_incremented(&self) { todo!() }
512    ///     # fn property_get_fallback(&self, _: StringName) -> Option<Variant> { todo!() }
513    ///     # fn property_set_fallback(_: SiMut<'_, Self>, _: StringName, _: &Variant) -> bool { todo!() }
514    ///     # fn get_method_argument_count(&self, _: StringName) -> Option<u32> { todo!() }
515    /// }
516    /// ```
517    pub fn base_mut(&mut self) -> ScriptBaseMut<'_, T> {
518        let guard = self.cell.make_inaccessible(self.mut_ref).unwrap();
519        let passive_gd = self.base_ref.to_script_passive();
520
521        ScriptBaseMut::new(passive_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        pub unsafe fn list_from_sys(&self, ptr: *const T) -> Box<[T]> {
588            let ptr: *mut T = ptr.cast_mut();
589            let len = self
590                .list_lengths
591                .lock()
592                .unwrap()
593                .remove(&ptr)
594                .expect("attempted to free list from wrong collection, this is a bug");
595            let len: usize = sys::conv::u32_to_usize(len);
596
597            // SAFETY: `ptr` was created in `list_into_sys` from a slice of length `len`.
598            // And has not been mutated since.
599            let slice = unsafe { std::slice::from_raw_parts_mut(ptr, len) };
600
601            // SAFETY: This is the first call to this function, and the list will not be accessed again after this function call.
602            unsafe { Box::from_raw(slice) }
603        }
604    }
605}
606
607#[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
608use self::bounded_ptr_list::BoundedPtrList;
609
610mod script_instance_info {
611    use std::any::type_name;
612    use std::ffi::c_void;
613
614    use sys::conv::{SYS_FALSE, SYS_TRUE, bool_to_sys};
615    #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
616    use sys::conv::{ptr_list_from_sys, ptr_list_into_sys};
617
618    use super::{ScriptInstance, ScriptInstanceData, SiMut};
619    use crate::builtin::{StringName, Variant};
620    use crate::private::{PanicPayload, handle_panic};
621    use crate::registry::info::{MethodInfo, PropertyInfo};
622    use crate::sys;
623
624    /// # Safety
625    ///
626    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
627    /// - `p_name` must be a valid [`StringName`] pointer.
628    /// - `p_value` must be a valid [`Variant`] pointer.
629    pub(super) unsafe extern "C" fn set_property_func<T: ScriptInstance>(
630        p_instance: sys::GDExtensionScriptInstanceDataPtr,
631        p_name: sys::GDExtensionConstStringNamePtr,
632        p_value: sys::GDExtensionConstVariantPtr,
633    ) -> sys::GDExtensionBool {
634        let (instance, name, value);
635        // SAFETY: `p_instance` is valid for this call; `p_name` and `p_value` are valid `StringName` and `Variant` pointers.
636        unsafe {
637            instance = ScriptInstanceData::<T>::borrow_script_sys(p_instance);
638            name = StringName::new_from_string_sys(p_name);
639            value = Variant::borrow_var_sys(p_value);
640        }
641        let result = with_instance_mut(instance, "set", |i| {
642            ScriptInstance::set_property(i, name, value)
643        })
644        // Unwrapping to a default of false, to indicate that the assignment is not handled by the script.
645        .unwrap_or_default();
646
647        bool_to_sys(result)
648    }
649
650    /// # Safety
651    ///
652    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
653    /// - `p_name` must be a valid [`StringName`] pointer.
654    /// - It must be safe to move a `Variant` into `r_ret`.
655    pub(super) unsafe extern "C" fn get_property_func<T: ScriptInstance>(
656        p_instance: sys::GDExtensionScriptInstanceDataPtr,
657        p_name: sys::GDExtensionConstStringNamePtr,
658        r_ret: sys::GDExtensionVariantPtr,
659    ) -> sys::GDExtensionBool {
660        let (instance, name);
661        // SAFETY: `p_instance` is valid for this call; `p_name` is a valid `StringName` pointer.
662        unsafe {
663            instance = ScriptInstanceData::<T>::borrow_script_sys(p_instance);
664            name = StringName::new_from_string_sys(p_name);
665        }
666        let return_value = with_instance(instance, "get", |i| i.get_property(name));
667
668        match return_value {
669            Ok(Some(variant)) => {
670                // SAFETY: It is safe to move a `Variant` into `r_ret`.
671                unsafe { variant.move_into_var_ptr(r_ret) };
672                SYS_TRUE
673            }
674            _ => SYS_FALSE,
675        }
676    }
677
678    /// # Safety
679    ///
680    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
681    /// - It must be safe to assign a `u32` to `r_count`.
682    pub(super) unsafe extern "C" fn get_property_list_func<T: ScriptInstance>(
683        p_instance: sys::GDExtensionScriptInstanceDataPtr,
684        r_count: *mut u32,
685    ) -> *const sys::GDExtensionPropertyInfo {
686        // SAFETY: `p_instance` is valid for this call.
687        let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
688        let property_list = with_instance(instance, "get_property_list", |i| {
689            i.get_property_list()
690                .into_iter()
691                .map(|prop| prop.into_owned_property_sys())
692                .collect::<Vec<_>>()
693        })
694        .unwrap_or_default();
695
696        #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
697        let (list_ptr, list_length) = instance.property_lists.list_into_sys(property_list);
698
699        #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
700        let (list_ptr, list_length) = ptr_list_into_sys(property_list);
701
702        // SAFETY: It is safe to assign a `u32` to `r_count`.
703        unsafe {
704            *r_count = list_length;
705        }
706
707        list_ptr
708    }
709
710    /// # Safety
711    ///
712    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
713    /// - `r_count` is expected to be a valid pointer to an u32.
714    pub(super) unsafe extern "C" fn get_method_list_func<T: ScriptInstance>(
715        p_instance: sys::GDExtensionScriptInstanceDataPtr,
716        r_count: *mut u32,
717    ) -> *const sys::GDExtensionMethodInfo {
718        // SAFETY: `p_instance` is valid for this call.
719        let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
720        let method_list = with_instance(instance, "get_method_list", |i| {
721            i.get_method_list()
722                .into_iter()
723                .map(|method| method.into_owned_method_sys())
724                .collect()
725        })
726        .unwrap_or_default();
727
728        #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
729        let (return_pointer, list_length) = instance.method_lists.list_into_sys(method_list);
730        #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
731        let (return_pointer, list_length) = ptr_list_into_sys(method_list);
732
733        unsafe {
734            *r_count = list_length;
735        }
736
737        return_pointer
738    }
739
740    /// Provides the same functionality as the function below, but for Godot 4.2 and lower.
741    ///
742    /// # Safety
743    ///
744    /// See latest version below.
745    #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
746    pub(super) unsafe extern "C" fn free_property_list_func<T: ScriptInstance>(
747        p_instance: sys::GDExtensionScriptInstanceDataPtr,
748        p_prop_info: *const sys::GDExtensionPropertyInfo,
749    ) {
750        let (instance, property_infos);
751        // SAFETY: `p_instance` is valid for this call; `p_prop_info` was returned from `list_into_sys` and has not been mutated since.
752        unsafe {
753            instance = ScriptInstanceData::<T>::borrow_script_sys(p_instance);
754            property_infos = instance.property_lists.list_from_sys(p_prop_info);
755        }
756
757        for info in property_infos.iter() {
758            // SAFETY: `info` was returned from a call to `into_owned_property_sys` and this is the first and only time this function is called
759            // on it.
760            unsafe { PropertyInfo::free_owned_property_sys(*info) };
761        }
762    }
763
764    /// # Safety
765    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
766    /// - `p_prop_info` must have been returned from a call to [`get_property_list_func`] called with the same `p_instance` pointer.
767    /// - `p_prop_info` must not have been mutated since the call to `get_property_list_func`.
768    #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
769    pub(super) unsafe extern "C" fn free_property_list_func(
770        _p_instance: sys::GDExtensionScriptInstanceDataPtr,
771        p_prop_info: *const sys::GDExtensionPropertyInfo,
772        p_len: u32,
773    ) {
774        // SAFETY: `p_prop_info` was returned from `list_into_sys` and has not been mutated since.
775        let property_infos = unsafe { ptr_list_from_sys(p_prop_info, p_len) };
776
777        for info in property_infos.iter() {
778            // SAFETY: `info` was returned from a call to `into_owned_property_sys` and this is the first and only time this function is called
779            // on it.
780            unsafe { PropertyInfo::free_owned_property_sys(*info) };
781        }
782    }
783
784    /// # Safety
785    ///
786    /// - `p_self` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
787    /// - `p_method` must be a valid [`StringName`] pointer.
788    /// - `p_args` has to point to a list of Variant pointers of length `p_argument_count`.
789    /// - All the variant pointers in `p_args`, as well the `p_args` pointer itself must be dereferenceable to an immutable reference for the
790    ///   duration of this call.
791    /// - It must be safe to move a [`Variant`] into `r_return`.
792    /// - `r_error` must point to an initialized [`sys::GDExtensionCallError`] which can be written to.
793    pub(super) unsafe extern "C" fn call_func<T: ScriptInstance>(
794        p_self: sys::GDExtensionScriptInstanceDataPtr,
795        p_method: sys::GDExtensionConstStringNamePtr,
796        p_args: *const sys::GDExtensionConstVariantPtr,
797        p_argument_count: sys::GDExtensionInt,
798        r_return: sys::GDExtensionVariantPtr,
799        r_error: *mut sys::GDExtensionCallError,
800    ) {
801        let (instance, method, args);
802        // SAFETY: `p_self` is valid during call; `p_method` is a valid `StringName` pointer; `p_args` is a valid array of len `p_argument_count`.
803        unsafe {
804            instance = ScriptInstanceData::<T>::borrow_script_sys(p_self);
805            method = StringName::new_from_string_sys(p_method);
806            args = Variant::borrow_ref_slice(
807                p_args,
808                p_argument_count
809                    .try_into()
810                    .expect("argument count should be a valid `u32`"),
811            );
812        }
813        let result = with_instance_mut(instance, "call", |i| {
814            ScriptInstance::call(i, method.clone(), args)
815        });
816
817        let error = match result {
818            Ok(Ok(variant)) => {
819                // SAFETY: It is safe to move a `Variant` into `r_return`.
820                unsafe { variant.move_into_var_ptr(r_return) };
821                sys::GDEXTENSION_CALL_OK
822            }
823
824            Ok(Err(err)) => err.to_sys(),
825
826            Err(_) => sys::GDEXTENSION_CALL_ERROR_INVALID_METHOD,
827        };
828
829        // SAFETY: `r_error` is an initialized pointer which we can write to.
830        unsafe { (*r_error).error = error };
831    }
832
833    /// Ownership of the returned object is not transferred to the caller. The caller is therefore responsible for incrementing the reference
834    /// count.
835    ///
836    /// # Safety
837    ///
838    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
839    pub(super) unsafe extern "C" fn get_script_func<T: ScriptInstance>(
840        p_instance: sys::GDExtensionScriptInstanceDataPtr,
841    ) -> sys::GDExtensionObjectPtr {
842        // SAFETY: `p_instance` is valid for this call.
843        let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
844        let script = with_instance(instance, "get_script", |i| i.get_script().clone());
845
846        match script {
847            Ok(script) => script.obj_sys(),
848            Err(_) => std::ptr::null_mut(),
849        }
850    }
851
852    /// # Safety
853    ///
854    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
855    pub(super) unsafe extern "C" fn is_placeholder_func<T: ScriptInstance>(
856        p_instance: sys::GDExtensionScriptInstanceDataPtr,
857    ) -> sys::GDExtensionBool {
858        // SAFETY: `p_instance` is valid for this call.
859        let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
860        let is_placeholder =
861            with_instance(instance, "is_placeholder", |i| i.is_placeholder()).unwrap_or_default();
862
863        bool_to_sys(is_placeholder)
864    }
865
866    /// # Safety
867    ///
868    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
869    /// - `p_method` has to point to a valid `StringName`.
870    pub(super) unsafe extern "C" fn has_method_func<T: ScriptInstance>(
871        p_instance: sys::GDExtensionScriptInstanceDataPtr,
872        p_method: sys::GDExtensionConstStringNamePtr,
873    ) -> sys::GDExtensionBool {
874        let (instance, method);
875        // SAFETY: `p_instance` is valid for this call; `p_method` is a valid `StringName` pointer.
876        unsafe {
877            instance = ScriptInstanceData::<T>::borrow_script_sys(p_instance);
878            method = StringName::new_from_string_sys(p_method);
879        }
880        let has_method =
881            with_instance(instance, "has_method", |i| i.has_method(method)).unwrap_or_default();
882
883        bool_to_sys(has_method)
884    }
885
886    /// Provides the same functionality as the function below, but for Godot 4.2 and lower.
887    ///
888    /// # Safety
889    ///
890    /// See latest version below.
891    #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
892    pub(super) unsafe extern "C" fn free_method_list_func<T: ScriptInstance>(
893        p_instance: sys::GDExtensionScriptInstanceDataPtr,
894        p_method_info: *const sys::GDExtensionMethodInfo,
895    ) {
896        let (instance, method_infos);
897        // SAFETY: `p_instance` is valid for this call; `p_method_info` was returned from `list_into_sys` and has not been mutated since.
898        unsafe {
899            instance = ScriptInstanceData::<T>::borrow_script_sys(p_instance);
900            method_infos = instance.method_lists.list_from_sys(p_method_info);
901        }
902
903        for info in method_infos.iter() {
904            // 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
905            // it.
906            unsafe { MethodInfo::free_owned_method_sys(*info) };
907        }
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_info` must have been returned from a call to [`get_method_list_func`] called with the same `p_instance` pointer.
914    /// - `p_method_info` must not have been mutated since the call to `get_method_list_func`.
915    #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
916    pub(super) unsafe extern "C" fn free_method_list_func(
917        _p_instance: sys::GDExtensionScriptInstanceDataPtr,
918        p_method_info: *const sys::GDExtensionMethodInfo,
919        p_len: u32,
920    ) {
921        // SAFETY: `p_method_info` was returned from `list_into_sys` and has not been mutated since.
922        let method_infos = unsafe { ptr_list_from_sys(p_method_info, p_len) };
923
924        for info in method_infos.iter() {
925            // 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
926            // it.
927            unsafe { MethodInfo::free_owned_method_sys(*info) };
928        }
929    }
930
931    /// # Safety
932    ///
933    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
934    /// - `p_name` must be a valid [`StringName`] pointer.
935    /// - `r_is_valid` must be assignable.
936    pub(super) unsafe extern "C" fn get_property_type_func<T: ScriptInstance>(
937        p_instance: sys::GDExtensionScriptInstanceDataPtr,
938        p_name: sys::GDExtensionConstStringNamePtr,
939        r_is_valid: *mut sys::GDExtensionBool,
940    ) -> sys::GDExtensionVariantType {
941        let (instance, name);
942        // SAFETY: `p_instance` is valid for this call; `p_name` is a valid `StringName` pointer.
943        unsafe {
944            instance = ScriptInstanceData::<T>::borrow_script_sys(p_instance);
945            name = StringName::new_from_string_sys(p_name);
946        }
947        let result = with_instance(instance, "get_property_type", |i| {
948            i.get_property_type(name.clone())
949        });
950
951        let (is_valid, result) = if let Ok(result) = result {
952            (SYS_TRUE, result.sys())
953        } else {
954            (SYS_FALSE, sys::GDEXTENSION_VARIANT_TYPE_NIL)
955        };
956
957        // SAFETY: `r_is_valid` is assignable.
958        unsafe { *r_is_valid = is_valid };
959        result
960    }
961
962    /// # Safety
963    ///
964    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
965    /// - `r_is_valid` must be assignable.
966    /// - It must be safe to move a [`GString`](crate::builtin::GString) into `r_str`.
967    pub(super) unsafe extern "C" fn to_string_func<T: ScriptInstance>(
968        p_instance: sys::GDExtensionScriptInstanceDataPtr,
969        r_is_valid: *mut sys::GDExtensionBool,
970        r_str: sys::GDExtensionStringPtr,
971    ) {
972        // SAFETY: `p_instance` is valid for this call.
973        let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
974        let string = with_instance(instance, "to_string", |i| i.to_string()).ok();
975
976        let Some(string) = string else {
977            return;
978        };
979
980        // SAFETY: `r_is_valid` is assignable.
981        unsafe { *r_is_valid = SYS_TRUE };
982        // SAFETY: It is safe to move a `GString` into `r_str`.
983        unsafe { string.move_into_string_ptr(r_str) };
984    }
985
986    /// # Safety
987    ///
988    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
989    ///
990    /// If `property_state_add` is non-null, then:
991    /// - It is safe to call `property_state_add` using the provided `userdata`.
992    /// - `property_state_add` must take ownership of the `StringName` and `Variant` it is called with.
993    pub(super) unsafe extern "C" fn get_property_state_func<T: ScriptInstance>(
994        p_instance: sys::GDExtensionScriptInstanceDataPtr,
995        property_state_add: sys::GDExtensionScriptInstancePropertyStateAdd,
996        userdata: *mut c_void,
997    ) {
998        // SAFETY: `p_instance` is valid for this call.
999        let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
1000        let property_states =
1001            with_instance(instance, "get_property_state", |i| i.get_property_state())
1002                .unwrap_or_default();
1003
1004        let Some(property_state_add) = property_state_add else {
1005            return;
1006        };
1007
1008        for (name, value) in property_states {
1009            // SAFETY: `property_state_add` is non-null, therefore we can call the function with the provided `userdata`.
1010            // Additionally `property_state_add` takes ownership of `name` and `value`.
1011            unsafe {
1012                property_state_add(
1013                    name.into_owned_string_sys(),
1014                    value.into_owned_var_sys(),
1015                    userdata,
1016                )
1017            }
1018        }
1019    }
1020
1021    /// Ownership of the returned object is not transferred to the caller. The caller must therefore ensure it's not freed when used.
1022    ///
1023    /// # Safety
1024    ///
1025    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
1026    pub(super) unsafe extern "C" fn get_language_func<T: ScriptInstance>(
1027        p_instance: sys::GDExtensionScriptInstanceDataPtr,
1028    ) -> sys::GDExtensionScriptLanguagePtr {
1029        // SAFETY: `p_instance` is valid for this call.
1030        let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
1031        let language = with_instance(instance, "get_language", |i| i.get_language());
1032
1033        if let Ok(language) = language {
1034            language.obj_sys().cast()
1035        } else {
1036            std::ptr::null_mut()
1037        }
1038    }
1039
1040    /// # Safety
1041    ///
1042    /// - `p_instance` must fulfill the safety preconditions of [`Box::from_raw`] for `Box<ScriptInstanceData<T>>`.
1043    pub(super) unsafe extern "C" fn free_func<T: ScriptInstance>(
1044        p_instance: sys::GDExtensionScriptInstanceDataPtr,
1045    ) {
1046        unsafe { drop(Box::from_raw(p_instance.cast::<ScriptInstanceData<T>>())) }
1047    }
1048
1049    /// # Safety
1050    ///
1051    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
1052    pub(super) unsafe extern "C" fn refcount_decremented_func<T: ScriptInstance>(
1053        p_instance: sys::GDExtensionScriptInstanceDataPtr,
1054    ) -> sys::GDExtensionBool {
1055        // SAFETY: `p_instance` is valid for this call.
1056        let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
1057        let result = with_instance(instance, "refcount_decremented", |i| {
1058            i.on_refcount_decremented()
1059        })
1060        .unwrap_or(true);
1061
1062        bool_to_sys(result)
1063    }
1064
1065    /// # Safety
1066    ///
1067    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
1068    pub(super) unsafe extern "C" fn refcount_incremented_func<T: ScriptInstance>(
1069        p_instance: sys::GDExtensionScriptInstanceDataPtr,
1070    ) {
1071        // SAFETY: `p_instance` is valid for this call.
1072        let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
1073        with_instance(instance, "refcount_incremented", |i| {
1074            i.on_refcount_incremented()
1075        })
1076        .unwrap_or_default();
1077    }
1078
1079    /// # Safety
1080    ///
1081    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
1082    /// - `p_name` must be a valid [`StringName`] pointer.
1083    /// - It must be safe to move a `Variant` into `r_ret`.
1084    pub(super) unsafe extern "C" fn get_fallback_func<T: ScriptInstance>(
1085        p_instance: sys::GDExtensionScriptInstanceDataPtr,
1086        p_name: sys::GDExtensionConstStringNamePtr,
1087        r_ret: sys::GDExtensionVariantPtr,
1088    ) -> sys::GDExtensionBool {
1089        let (instance, name);
1090        // SAFETY: `p_instance` is valid for this call; `p_name` is a valid `StringName` pointer.
1091        unsafe {
1092            instance = ScriptInstanceData::<T>::borrow_script_sys(p_instance);
1093            name = StringName::new_from_string_sys(p_name);
1094        }
1095
1096        let return_value = with_instance(instance, "property_get_fallback", |i| {
1097            i.property_get_fallback(name)
1098        });
1099
1100        match return_value {
1101            Ok(Some(variant)) => {
1102                // SAFETY: It is safe to move a `Variant` into `r_ret`.
1103                unsafe { variant.move_into_var_ptr(r_ret) };
1104                SYS_TRUE
1105            }
1106            _ => SYS_FALSE,
1107        }
1108    }
1109
1110    /// # Safety
1111    ///
1112    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
1113    /// - `p_name` must be a valid [`StringName`] pointer.
1114    /// - `p_value` must be a valid [`Variant`] pointer.
1115    pub(super) unsafe extern "C" fn set_fallback_func<T: ScriptInstance>(
1116        p_instance: sys::GDExtensionScriptInstanceDataPtr,
1117        p_name: sys::GDExtensionConstStringNamePtr,
1118        p_value: sys::GDExtensionConstVariantPtr,
1119    ) -> sys::GDExtensionBool {
1120        let (instance, name, value);
1121        // SAFETY: `p_instance` is valid for this call; `p_name` and `p_value` are valid `StringName` and `Variant` pointers.
1122        unsafe {
1123            instance = ScriptInstanceData::<T>::borrow_script_sys(p_instance);
1124            name = StringName::new_from_string_sys(p_name);
1125            value = Variant::borrow_var_sys(p_value);
1126        }
1127
1128        let result = with_instance_mut(instance, "property_set_fallback", |i| {
1129            ScriptInstance::property_set_fallback(i, name, value)
1130        })
1131        .unwrap_or_default();
1132
1133        bool_to_sys(result)
1134    }
1135
1136    /// # Safety
1137    ///
1138    /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
1139    /// - `p_method` has to point to a valid [`StringName`].
1140    /// - `p_value` must be a valid [`sys::GDExtensionBool`] pointer.
1141    #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
1142    pub(super) unsafe extern "C" fn get_method_argument_count_func<T: ScriptInstance>(
1143        p_instance: sys::GDExtensionScriptInstanceDataPtr,
1144        p_method: sys::GDExtensionConstStringNamePtr,
1145        r_is_valid: *mut sys::GDExtensionBool,
1146    ) -> sys::GDExtensionInt {
1147        let (instance, method);
1148        // SAFETY: `p_instance` is valid for this call; `p_method` is a valid `StringName` pointer.
1149        unsafe {
1150            instance = ScriptInstanceData::<T>::borrow_script_sys(p_instance);
1151            method = StringName::new_from_string_sys(p_method);
1152        }
1153        // 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.
1154        let method_argument_count = with_instance(instance, "get_method_argument_count", |i| {
1155            i.get_method_argument_count(method)
1156        })
1157        .unwrap_or_default();
1158
1159        let (result, is_valid) = match method_argument_count {
1160            Some(count) => (count, SYS_TRUE),
1161            None => (0, SYS_FALSE),
1162        };
1163
1164        // SAFETY: `r_is_valid` is assignable.
1165        unsafe { *r_is_valid = is_valid };
1166
1167        result.into()
1168    }
1169
1170    fn error_ctx<T: ScriptInstance>(method: &'static str) -> impl Fn() -> String {
1171        move || format!("{}::{method}(), script instance method", type_name::<T>())
1172    }
1173
1174    fn with_instance<T: ScriptInstance, R>(
1175        instance: &ScriptInstanceData<T>,
1176        method: &'static str,
1177        f: impl FnOnce(&T) -> R + std::panic::UnwindSafe,
1178    ) -> Result<R, PanicPayload> {
1179        handle_panic(error_ctx::<T>(method), || f(&instance.borrow()))
1180    }
1181
1182    fn with_instance_mut<T: ScriptInstance, R>(
1183        instance: &ScriptInstanceData<T>,
1184        method: &'static str,
1185        f: impl FnOnce(SiMut<T>) -> R + std::panic::UnwindSafe,
1186    ) -> Result<R, PanicPayload> {
1187        handle_panic(error_ctx::<T>(method), || {
1188            let mut guard = instance.borrow_mut();
1189            let instance_guard = SiMut::new(instance.cell_ref(), &mut guard, &instance.base);
1190            f(instance_guard)
1191        })
1192    }
1193}