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