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}