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