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"))] #[cfg_attr(published_docs, doc(cfg(not(feature = "experimental-threads"))))]
22use godot_cell::panicking::{GdCell, MutGuard, RefGuard};
23
24#[cfg(feature = "experimental-threads")] #[cfg_attr(published_docs, doc(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")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
34use self::bounded_ptr_list::BoundedPtrList;
35
36#[cfg(since_api = "4.2")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.2")))]
37use crate::classes::IScriptExtension;
38#[cfg(since_api = "4.2")] #[cfg_attr(published_docs, doc(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. This pointer should then be
46/// returned from [`IScriptExtension::instance_create_rawptr()`](crate::classes::IScriptExtension::instance_create_rawptr).
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, routing property writes to a fallback if applicable.
124 ///
125 /// If this function and [IScriptExtension::is_placeholder_fallback_enabled] return true, Godot will call [`Self::property_set_fallback`]
126 /// instead of [`Self::set_property`].
127 fn is_placeholder(&self) -> bool;
128
129 /// Validation function for the engine to verify if the script exposes a certain method.
130 fn has_method(&self, method: StringName) -> bool;
131
132 /// Lets the engine get a reference to the script this instance was created for.
133 ///
134 /// This function has to return a reference, because scripts are reference-counted in Godot, and it must be guaranteed that the object is
135 /// not freed before the engine increased the reference count. (Every time a ref-counted `Gd<T>` is dropped, the reference count is
136 /// decremented.)
137 fn get_script(&self) -> &Gd<Script>;
138
139 /// Lets the engine fetch the type of a particular property.
140 fn get_property_type(&self, name: StringName) -> VariantType;
141
142 /// String representation of the script instance.
143 fn to_string(&self) -> GString;
144
145 /// A dump of all property names and values that are exposed to the engine.
146 fn get_property_state(&self) -> Vec<(StringName, Variant)>;
147
148 /// Lets the engine get a reference to the [`ScriptLanguage`] this instance belongs to.
149 fn get_language(&self) -> Gd<ScriptLanguage>;
150
151 /// Callback from the engine when the reference count of the base object has been decreased. When this method returns `true` the engine will
152 /// not free the object the script is attached to.
153 fn on_refcount_decremented(&self) -> bool;
154
155 /// Callback from the engine when the reference count of the base object has been increased.
156 fn on_refcount_incremented(&self);
157
158 /// The engine may call this function if it failed to get a property value via [`ScriptInstance::get_property`] or the native type's getter.
159 fn property_get_fallback(&self, name: StringName) -> Option<Variant>;
160
161 /// The engine may call this function if [`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")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
169 fn get_method_argument_count(&self, _method: StringName) -> Option<u32>;
170}
171
172#[cfg(before_api = "4.2")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.2")))]
173type ScriptInstanceInfo = sys::GDExtensionScriptInstanceInfo;
174#[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"))))]
175type ScriptInstanceInfo = sys::GDExtensionScriptInstanceInfo2;
176#[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(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")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
183 property_lists: BoundedPtrList<sys::GDExtensionPropertyInfo>,
184 #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(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")] #[cfg_attr(published_docs, doc(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")] #[cfg_attr(published_docs, doc(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")] #[cfg_attr(published_docs, doc(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")] #[cfg_attr(published_docs, doc(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")] #[cfg_attr(published_docs, doc(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")] #[cfg_attr(published_docs, doc(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")] #[cfg_attr(published_docs, doc(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")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
314 property_lists: BoundedPtrList::new(),
315 #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(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")] #[cfg_attr(published_docs, doc(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"))] #[cfg_attr(published_docs, doc(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")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
336 let create_fn = sys::interface_fn!(script_instance_create3);
337
338 create_fn(
339 instance_ptr,
340 data_ptr as sys::GDExtensionScriptInstanceDataPtr,
341 ) 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`].
351#[cfg(since_api = "4.2")] #[cfg_attr(published_docs, doc(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 .is_none_or(|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 /// You have to keep the `ScriptBaseRef` guard bound for the entire duration the engine might re-enter a function of your
461 /// `ScriptInstance`. The guard temporarily absorbs the `&mut self` reference, which allows for an additional mutable reference to be
462 /// acquired.
463 ///
464 /// Holding a mutable guard prevents other code paths from obtaining _any_ reference to `self`, as such it is recommended to drop the
465 /// guard as soon as you no longer need it.
466 ///
467 /// ```no_run
468 /// # use godot::prelude::*;
469 /// # use godot::classes::{ScriptLanguage, Script};
470 /// # use godot::obj::script::{ScriptInstance, SiMut};
471 /// # use godot::meta::{MethodInfo, PropertyInfo};
472 /// # use godot::sys;
473 /// struct ExampleScriptInstance;
474 ///
475 /// impl ScriptInstance for ExampleScriptInstance {
476 /// type Base = Object;
477 ///
478 /// fn call(
479 /// mut this: SiMut<Self>,
480 /// method: StringName,
481 /// args: &[&Variant],
482 /// ) -> Result<Variant, sys::GDExtensionCallErrorType> {
483 /// // Check whether method is available on this script
484 /// if method == StringName::from("script_method") {
485 /// godot_print!("script_method called!");
486 /// return Ok(true.to_variant());
487 /// }
488 ///
489 /// let node = Node::new_alloc();
490 ///
491 /// // We can call back into `self` through Godot:
492 /// this.base_mut().call("script_method", &[]);
493 ///
494 /// Ok(Variant::nil())
495 /// }
496 /// # fn class_name(&self) -> GString { todo!() }
497 /// # fn set_property(_: SiMut<'_, Self>, _: StringName, _: &Variant) -> bool { todo!() }
498 /// # fn get_property(&self, _: StringName) -> Option<Variant> { todo!() }
499 /// # fn get_property_list(&self) -> Vec<PropertyInfo> { todo!() }
500 /// # fn get_method_list(&self) -> Vec<MethodInfo> { todo!() }
501 /// # fn is_placeholder(&self) -> bool { todo!() }
502 /// # fn has_method(&self, _: StringName) -> bool { todo!() }
503 /// # fn get_script(&self) -> &Gd<Script> { todo!() }
504 /// # fn get_property_type(&self, _: StringName) -> VariantType { todo!() }
505 /// # fn to_string(&self) -> GString { todo!() }
506 /// # fn get_property_state(&self) -> Vec<(StringName, Variant)> { todo!() }
507 /// # fn get_language(&self) -> Gd<ScriptLanguage> { todo!() }
508 /// # fn on_refcount_decremented(&self) -> bool { todo!() }
509 /// # fn on_refcount_incremented(&self) { todo!() }
510 /// # fn property_get_fallback(&self, _: StringName) -> Option<Variant> { todo!() }
511 /// # fn property_set_fallback(_: SiMut<'_, Self>, _: StringName, _: &Variant) -> bool { todo!() }
512 /// # fn get_method_argument_count(&self, _: StringName) -> Option<u32> { todo!() }
513 /// }
514 /// ```
515 pub fn base_mut(&mut self) -> ScriptBaseMut<T> {
516 let guard = self.cell.make_inaccessible(self.mut_ref).unwrap();
517
518 ScriptBaseMut::new(self.base_ref.to_gd(), guard)
519 }
520}
521
522impl<T: ScriptInstance> Deref for SiMut<'_, T> {
523 type Target = T;
524
525 fn deref(&self) -> &Self::Target {
526 self.mut_ref
527 }
528}
529
530impl<T: ScriptInstance> DerefMut for SiMut<'_, T> {
531 fn deref_mut(&mut self) -> &mut Self::Target {
532 self.mut_ref
533 }
534}
535
536// Encapsulate BoundedPtrList to help ensure safety.
537#[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
538mod bounded_ptr_list {
539 use std::collections::HashMap;
540 use std::sync::Mutex;
541
542 use godot_ffi as sys;
543
544 /// Helper struct to store the lengths of lists, so they can be properly freed.
545 ///
546 /// This uses the term `list` because it refers to property/method lists in gdextension.
547 pub struct BoundedPtrList<T> {
548 list_lengths: Mutex<HashMap<*mut T, u32>>,
549 }
550
551 impl<T> BoundedPtrList<T> {
552 pub fn new() -> Self {
553 Self {
554 list_lengths: Mutex::new(HashMap::new()),
555 }
556 }
557
558 /// Convert a list into a pointer + length pair. Should be used together with [`list_from_sys`](Self::list_from_sys).
559 ///
560 /// If `list_from_sys` is not called on this list then that will cause a memory leak.
561 pub fn list_into_sys(&self, list: Vec<T>) -> (*const T, u32) {
562 let len: u32 = list
563 .len()
564 .try_into()
565 .expect("list must have length that fits in u32");
566 let ptr = Box::leak(list.into_boxed_slice()).as_mut_ptr();
567
568 let old_value = self.list_lengths.lock().unwrap().insert(ptr, len);
569 assert_eq!(
570 old_value, None,
571 "attempted to insert the same list twice, this is a bug"
572 );
573
574 (ptr.cast_const(), len)
575 }
576
577 /// Get a list back from a previous call to [`list_into_sys`](Self::list_into_sys).
578 ///
579 /// # Safety
580 /// - `ptr` must have been returned from a call to `list_into_sys` on `self`.
581 /// - `ptr` must not have been used in a call to this function before.
582 /// - `ptr` must not have been mutated since the call to `list_into_sys`.
583 /// - `ptr` must not be accessed after calling this function.
584 #[deny(unsafe_op_in_unsafe_fn)]
585 pub unsafe fn list_from_sys(&self, ptr: *const T) -> Box<[T]> {
586 let ptr: *mut T = ptr.cast_mut();
587 let len = self
588 .list_lengths
589 .lock()
590 .unwrap()
591 .remove(&ptr)
592 .expect("attempted to free list from wrong collection, this is a bug");
593 let len: usize = sys::conv::u32_to_usize(len);
594
595 // SAFETY: `ptr` was created in `list_into_sys` from a slice of length `len`.
596 // And has not been mutated since.
597 let slice = unsafe { std::slice::from_raw_parts_mut(ptr, len) };
598
599 // SAFETY: This is the first call to this function, and the list will not be accessed again after this function call.
600 unsafe { Box::from_raw(slice) }
601 }
602 }
603}
604
605#[deny(unsafe_op_in_unsafe_fn)]
606mod script_instance_info {
607 use std::any::type_name;
608 use std::ffi::c_void;
609
610 use crate::builtin::{StringName, Variant};
611 use crate::private::handle_panic;
612 use crate::sys;
613
614 use super::{ScriptInstance, ScriptInstanceData, SiMut};
615 use crate::meta::{MethodInfo, PropertyInfo};
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 /// # Safety
621 ///
622 /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
623 /// - `p_name` must be a valid [`StringName`] pointer.
624 /// - `p_value` must be a valid [`Variant`] pointer.
625 pub(super) unsafe extern "C" fn set_property_func<T: ScriptInstance>(
626 p_instance: sys::GDExtensionScriptInstanceDataPtr,
627 p_name: sys::GDExtensionConstStringNamePtr,
628 p_value: sys::GDExtensionConstVariantPtr,
629 ) -> sys::GDExtensionBool {
630 let (name, value);
631 // SAFETY: `p_name` and `p_value` are valid pointers to a `StringName` and `Variant`.
632 unsafe {
633 name = StringName::new_from_string_sys(p_name);
634 value = Variant::borrow_var_sys(p_value);
635 }
636 let ctx = || format!("error when calling {}::set", type_name::<T>());
637
638 let result = handle_panic(ctx, || {
639 // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
640 let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
641 let mut guard = instance.borrow_mut();
642
643 let instance_guard = SiMut::new(instance.cell_ref(), &mut guard, &instance.base);
644
645 ScriptInstance::set_property(instance_guard, name, value)
646 })
647 // Unwrapping to a default of false, to indicate that the assignment is not handled by the script.
648 .unwrap_or_default();
649
650 bool_to_sys(result)
651 }
652
653 /// # Safety
654 ///
655 /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
656 /// - `p_name` must be a valid [`StringName`] pointer.
657 /// - It must be safe to move a `Variant` into `r_ret`.
658 pub(super) unsafe extern "C" fn get_property_func<T: ScriptInstance>(
659 p_instance: sys::GDExtensionScriptInstanceDataPtr,
660 p_name: sys::GDExtensionConstStringNamePtr,
661 r_ret: sys::GDExtensionVariantPtr,
662 ) -> sys::GDExtensionBool {
663 // SAFETY: `p_name` is a valid [`StringName`] pointer.
664 let name = unsafe { StringName::new_from_string_sys(p_name) };
665 let ctx = || format!("error when calling {}::get", type_name::<T>());
666
667 let return_value = handle_panic(ctx, || {
668 // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
669 unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) }
670 .borrow()
671 .get_property(name)
672 });
673
674 match return_value {
675 Ok(Some(variant)) => {
676 // SAFETY: It is safe to move a `Variant` into `r_ret`.
677 unsafe { variant.move_into_var_ptr(r_ret) };
678 SYS_TRUE
679 }
680 _ => SYS_FALSE,
681 }
682 }
683
684 /// # Safety
685 ///
686 /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
687 /// - It must be safe to assign a `u32` to `r_count`.
688 pub(super) unsafe extern "C" fn get_property_list_func<T: ScriptInstance>(
689 p_instance: sys::GDExtensionScriptInstanceDataPtr,
690 r_count: *mut u32,
691 ) -> *const sys::GDExtensionPropertyInfo {
692 let ctx = || format!("error when calling {}::get_property_list", type_name::<T>());
693
694 // Encapsulate this unsafe block to avoid repeating the safety comment.
695 // SAFETY: This closure is only used in this function, and we may dereference `p_instance` to an immutable reference for the duration of
696 // this call.
697 let borrow_instance =
698 move || unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
699
700 let property_list = handle_panic(ctx, || {
701 let property_list = borrow_instance().borrow().get_property_list();
702
703 property_list
704 .into_iter()
705 .map(|prop| prop.into_owned_property_sys())
706 .collect::<Vec<_>>()
707 })
708 .unwrap_or_default();
709
710 #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
711 let (list_ptr, list_length) = borrow_instance()
712 .property_lists
713 .list_into_sys(property_list);
714
715 #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
716 let (list_ptr, list_length) = ptr_list_into_sys(property_list);
717
718 // SAFETY: It is safe to assign a `u32` to `r_count`.
719 unsafe {
720 *r_count = list_length;
721 }
722
723 list_ptr
724 }
725
726 /// # Safety
727 ///
728 /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
729 /// - `r_count` is expected to be a valid pointer to an u32.
730 pub(super) unsafe extern "C" fn get_method_list_func<T: ScriptInstance>(
731 p_instance: sys::GDExtensionScriptInstanceDataPtr,
732 r_count: *mut u32,
733 ) -> *const sys::GDExtensionMethodInfo {
734 let ctx = || format!("error when calling {}::get_method_list", type_name::<T>());
735
736 // Encapsulate this unsafe block to avoid repeating the safety comment.
737 // SAFETY: This closure is only used in this function, and we may dereference `p_instance` to an immutable reference for the duration of
738 // this call.
739 let borrow_instance =
740 move || unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
741
742 let method_list = handle_panic(ctx, || {
743 let method_list = borrow_instance().borrow().get_method_list();
744
745 method_list
746 .into_iter()
747 .map(|method| method.into_owned_method_sys())
748 .collect()
749 })
750 .unwrap_or_default();
751
752 #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
753 let (return_pointer, list_length) =
754 borrow_instance().method_lists.list_into_sys(method_list);
755 #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
756 let (return_pointer, list_length) = ptr_list_into_sys(method_list);
757
758 unsafe {
759 *r_count = list_length;
760 }
761
762 return_pointer
763 }
764
765 /// Provides the same functionality as the function below, but for Godot 4.2 and lower.
766 ///
767 /// # Safety
768 ///
769 /// See latest version below.
770 #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
771 pub(super) unsafe extern "C" fn free_property_list_func<T: ScriptInstance>(
772 p_instance: sys::GDExtensionScriptInstanceDataPtr,
773 p_prop_info: *const sys::GDExtensionPropertyInfo,
774 ) {
775 // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
776 let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
777
778 // 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
779 // to `list_from_sys` with this pointer.
780 let property_infos = unsafe { instance.property_lists.list_from_sys(p_prop_info) };
781
782 for info in property_infos.iter() {
783 // SAFETY: `info` was returned from a call to `into_owned_property_sys` and this is the first and only time this function is called
784 // on it.
785 unsafe { PropertyInfo::free_owned_property_sys(*info) };
786 }
787 }
788
789 /// # Safety
790 /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
791 /// - `p_prop_info` must have been returned from a call to [`get_property_list_func`] called with the same `p_instance` pointer.
792 /// - `p_prop_info` must not have been mutated since the call to `get_property_list_func`.
793 #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
794 pub(super) unsafe extern "C" fn free_property_list_func(
795 _p_instance: sys::GDExtensionScriptInstanceDataPtr,
796 p_prop_info: *const sys::GDExtensionPropertyInfo,
797 p_len: u32,
798 ) {
799 // 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
800 // to `list_from_sys` with this pointer.
801 let property_infos = unsafe { ptr_list_from_sys(p_prop_info, p_len) };
802
803 for info in property_infos.iter() {
804 // SAFETY: `info` was returned from a call to `into_owned_property_sys` and this is the first and only time this function is called
805 // on it.
806 unsafe { PropertyInfo::free_owned_property_sys(*info) };
807 }
808 }
809
810 /// # Safety
811 ///
812 /// - `p_self` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
813 /// - `p_method` must be a valid [`StringName`] pointer.
814 /// - `p_args` has to point to a list of Variant pointers of length `p_argument_count`.
815 /// - All the variant pointers in `p_args`, as well the `p_args` pointer itself must be dereferenceable to an immutable reference for the
816 /// duration of this call.
817 /// - It must be safe to move a [`Variant`] into `r_return`.
818 /// - `r_error` must point to an initialized [`sys::GDExtensionCallError`] which can be written to.
819 pub(super) unsafe extern "C" fn call_func<T: ScriptInstance>(
820 p_self: sys::GDExtensionScriptInstanceDataPtr,
821 p_method: sys::GDExtensionConstStringNamePtr,
822 p_args: *const sys::GDExtensionConstVariantPtr,
823 p_argument_count: sys::GDExtensionInt,
824 r_return: sys::GDExtensionVariantPtr,
825 r_error: *mut sys::GDExtensionCallError,
826 ) {
827 // SAFETY: `p_method` is a valid [`StringName`] pointer.
828 let method = unsafe { StringName::new_from_string_sys(p_method) };
829 // SAFETY: `p_args` is a valid array of length `p_argument_count`
830 let args = unsafe {
831 Variant::borrow_ref_slice(
832 p_args,
833 p_argument_count
834 .try_into()
835 .expect("argument count should be a valid `u32`"),
836 )
837 };
838 let ctx = || format!("error when calling {}::call", type_name::<T>());
839
840 let result = handle_panic(ctx, || {
841 // SAFETY: `p_self` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
842 let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_self) };
843 let mut guard = instance.borrow_mut();
844
845 let instance_guard = SiMut::new(instance.cell_ref(), &mut guard, &instance.base);
846
847 ScriptInstance::call(instance_guard, method.clone(), args)
848 });
849
850 let error = match result {
851 Ok(Ok(variant)) => {
852 // SAFETY: It is safe to move a `Variant` into `r_return`.
853 unsafe { variant.move_into_var_ptr(r_return) };
854 sys::GDEXTENSION_CALL_OK
855 }
856
857 Ok(Err(err)) => err,
858
859 Err(_) => sys::GDEXTENSION_CALL_ERROR_INVALID_METHOD,
860 };
861
862 // SAFETY: `r_error` is an initialized pointer which we can write to.
863 unsafe { (*r_error).error = error };
864 }
865
866 /// Ownership of the returned object is not transferred to the caller. The caller is therefore responsible for incrementing the reference
867 /// count.
868 ///
869 /// # Safety
870 ///
871 /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
872 pub(super) unsafe extern "C" fn get_script_func<T: ScriptInstance>(
873 p_instance: sys::GDExtensionScriptInstanceDataPtr,
874 ) -> sys::GDExtensionObjectPtr {
875 let ctx = || format!("error when calling {}::get_script", type_name::<T>());
876
877 let script = handle_panic(ctx, || {
878 // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
879 unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) }
880 .borrow()
881 .get_script()
882 .clone()
883 });
884
885 match script {
886 Ok(script) => script.obj_sys(),
887 Err(_) => std::ptr::null_mut(),
888 }
889 }
890
891 /// # Safety
892 ///
893 /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
894 pub(super) unsafe extern "C" fn is_placeholder_func<T: ScriptInstance>(
895 p_instance: sys::GDExtensionScriptInstanceDataPtr,
896 ) -> sys::GDExtensionBool {
897 let ctx = || format!("error when calling {}::is_placeholder", type_name::<T>());
898
899 let is_placeholder = handle_panic(ctx, || {
900 // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
901 unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) }
902 .borrow()
903 .is_placeholder()
904 })
905 .unwrap_or_default();
906
907 bool_to_sys(is_placeholder)
908 }
909
910 /// # Safety
911 ///
912 /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
913 /// - `p_method` has to point to a valid `StringName`.
914 pub(super) unsafe extern "C" fn has_method_func<T: ScriptInstance>(
915 p_instance: sys::GDExtensionScriptInstanceDataPtr,
916 p_method: sys::GDExtensionConstStringNamePtr,
917 ) -> sys::GDExtensionBool {
918 // SAFETY: `p_method` is a valid [`StringName`] pointer.
919 let method = unsafe { StringName::new_from_string_sys(p_method) };
920 let ctx = || format!("error when calling {}::has_method", type_name::<T>());
921
922 let has_method = handle_panic(ctx, || {
923 // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
924 unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) }
925 .borrow()
926 .has_method(method)
927 })
928 .unwrap_or_default();
929
930 bool_to_sys(has_method)
931 }
932
933 /// Provides the same functionality as the function below, but for Godot 4.2 and lower.
934 ///
935 /// # Safety
936 ///
937 /// See latest version below.
938 #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
939 pub(super) unsafe extern "C" fn free_method_list_func<T: ScriptInstance>(
940 p_instance: sys::GDExtensionScriptInstanceDataPtr,
941 p_method_info: *const sys::GDExtensionMethodInfo,
942 ) {
943 // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
944 let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
945
946 // 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
947 // to `list_from_sys` with this pointer.
948 let method_infos = unsafe { instance.method_lists.list_from_sys(p_method_info) };
949
950 for info in method_infos.iter() {
951 // 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
952 // it.
953 unsafe { MethodInfo::free_owned_method_sys(*info) };
954 }
955 }
956
957 /// # Safety
958 ///
959 /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
960 /// - `p_method_info` must have been returned from a call to [`get_method_list_func`] called with the same `p_instance` pointer.
961 /// - `p_method_info` must not have been mutated since the call to `get_method_list_func`.
962 #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
963 pub(super) unsafe extern "C" fn free_method_list_func(
964 _p_instance: sys::GDExtensionScriptInstanceDataPtr,
965 p_method_info: *const sys::GDExtensionMethodInfo,
966 p_len: u32,
967 ) {
968 // 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
969 // to `list_from_sys` with this pointer.
970 let method_infos = unsafe { ptr_list_from_sys(p_method_info, p_len) };
971
972 for info in method_infos.iter() {
973 // 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
974 // it.
975 unsafe { MethodInfo::free_owned_method_sys(*info) };
976 }
977 }
978
979 /// # Safety
980 ///
981 /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
982 /// - `p_name` must be a valid [`StringName`] pointer.
983 /// - `r_is_valid` must be assignable.
984 pub(super) unsafe extern "C" fn get_property_type_func<T: ScriptInstance>(
985 p_instance: sys::GDExtensionScriptInstanceDataPtr,
986 p_name: sys::GDExtensionConstStringNamePtr,
987 r_is_valid: *mut sys::GDExtensionBool,
988 ) -> sys::GDExtensionVariantType {
989 let ctx = || {
990 format!(
991 "error while calling {}::get_property_type",
992 type_name::<T>()
993 )
994 };
995 // SAFETY: `p_name` is a valid [`StringName`] pointer.
996 let name = unsafe { StringName::new_from_string_sys(p_name) };
997
998 let result = handle_panic(ctx, || {
999 // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
1000 unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) }
1001 .borrow()
1002 .get_property_type(name.clone())
1003 });
1004
1005 let (is_valid, result) = if let Ok(result) = result {
1006 (SYS_TRUE, result.sys())
1007 } else {
1008 (SYS_FALSE, sys::GDEXTENSION_VARIANT_TYPE_NIL)
1009 };
1010
1011 // SAFETY: `r_is_valid` is assignable.
1012 unsafe { *r_is_valid = is_valid };
1013 result
1014 }
1015
1016 /// # Safety
1017 ///
1018 /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
1019 /// - `r_is_valid` must be assignable.
1020 /// - It must be safe to move a [`GString`](crate::builtin::GString) into `r_str`.
1021 pub(super) unsafe extern "C" fn to_string_func<T: ScriptInstance>(
1022 p_instance: sys::GDExtensionScriptInstanceDataPtr,
1023 r_is_valid: *mut sys::GDExtensionBool,
1024 r_str: sys::GDExtensionStringPtr,
1025 ) {
1026 let ctx = || format!("error when calling {}::to_string", type_name::<T>());
1027
1028 let string = handle_panic(ctx, || {
1029 // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
1030 unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) }
1031 .borrow()
1032 .to_string()
1033 })
1034 .ok();
1035
1036 let Some(string) = string else {
1037 return;
1038 };
1039
1040 // SAFETY: `r_is_valid` is assignable.
1041 unsafe { *r_is_valid = SYS_TRUE };
1042 // SAFETY: It is safe to move a `GString` into `r_str`.
1043 unsafe { string.move_into_string_ptr(r_str) };
1044 }
1045
1046 /// # Safety
1047 ///
1048 /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
1049 ///
1050 /// If `property_state_add` is non-null, then:
1051 /// - It is safe to call `property_state_add` using the provided `userdata`.
1052 /// - `property_state_add` must take ownership of the `StringName` and `Variant` it is called with.
1053 pub(super) unsafe extern "C" fn get_property_state_func<T: ScriptInstance>(
1054 p_instance: sys::GDExtensionScriptInstanceDataPtr,
1055 property_state_add: sys::GDExtensionScriptInstancePropertyStateAdd,
1056 userdata: *mut c_void,
1057 ) {
1058 let ctx = || {
1059 format!(
1060 "error when calling {}::get_property_state",
1061 type_name::<T>()
1062 )
1063 };
1064
1065 let property_states = handle_panic(ctx, || {
1066 // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
1067 unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) }
1068 .borrow()
1069 .get_property_state()
1070 })
1071 .unwrap_or_default();
1072
1073 let Some(property_state_add) = property_state_add else {
1074 return;
1075 };
1076
1077 for (name, value) in property_states {
1078 // SAFETY: `property_state_add` is non-null, therefore we can call the function with the provided `userdata`.
1079 // Additionally `property_state_add` takes ownership of `name` and `value`.
1080 unsafe {
1081 property_state_add(
1082 name.into_owned_string_sys(),
1083 value.into_owned_var_sys(),
1084 userdata,
1085 )
1086 }
1087 }
1088 }
1089
1090 /// Ownership of the returned object is not transferred to the caller. The caller must therefore ensure it's not freed when used.
1091 ///
1092 /// # Safety
1093 ///
1094 /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
1095 pub(super) unsafe extern "C" fn get_language_func<T: ScriptInstance>(
1096 p_instance: sys::GDExtensionScriptInstanceDataPtr,
1097 ) -> sys::GDExtensionScriptLanguagePtr {
1098 let ctx = || format!("error when calling {}::get_language", type_name::<T>());
1099
1100 let language = handle_panic(ctx, || {
1101 // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
1102 unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) }
1103 .borrow()
1104 .get_language()
1105 });
1106
1107 if let Ok(language) = language {
1108 language.obj_sys().cast()
1109 } else {
1110 std::ptr::null_mut()
1111 }
1112 }
1113
1114 /// # Safety
1115 ///
1116 /// - `p_instance` must fulfill the safety preconditions of [`Box::from_raw`] for `Box<ScriptInstanceData<T>>`.
1117 pub(super) unsafe extern "C" fn free_func<T: ScriptInstance>(
1118 p_instance: sys::GDExtensionScriptInstanceDataPtr,
1119 ) {
1120 unsafe { drop(Box::from_raw(p_instance.cast::<ScriptInstanceData<T>>())) }
1121 }
1122
1123 /// # Safety
1124 ///
1125 /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
1126 pub(super) unsafe extern "C" fn refcount_decremented_func<T: ScriptInstance>(
1127 p_instance: sys::GDExtensionScriptInstanceDataPtr,
1128 ) -> sys::GDExtensionBool {
1129 let ctx = || {
1130 format!(
1131 "error when calling {}::refcount_decremented",
1132 type_name::<T>()
1133 )
1134 };
1135
1136 let result = handle_panic(ctx, || {
1137 // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
1138 unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) }
1139 .borrow()
1140 .on_refcount_decremented()
1141 })
1142 .unwrap_or(true);
1143
1144 bool_to_sys(result)
1145 }
1146
1147 /// # Safety
1148 ///
1149 /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
1150 pub(super) unsafe extern "C" fn refcount_incremented_func<T: ScriptInstance>(
1151 p_instance: sys::GDExtensionScriptInstanceDataPtr,
1152 ) {
1153 let ctx = || {
1154 format!(
1155 "error when calling {}::refcount_incremented",
1156 type_name::<T>()
1157 )
1158 };
1159
1160 handle_panic(ctx, || {
1161 // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
1162 unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) }
1163 .borrow()
1164 .on_refcount_incremented();
1165 })
1166 .unwrap_or_default();
1167 }
1168
1169 /// # Safety
1170 ///
1171 /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
1172 /// - `p_name` must be a valid [`StringName`] pointer.
1173 /// - It must be safe to move a `Variant` into `r_ret`.
1174 pub(super) unsafe extern "C" fn get_fallback_func<T: ScriptInstance>(
1175 p_instance: sys::GDExtensionScriptInstanceDataPtr,
1176 p_name: sys::GDExtensionConstStringNamePtr,
1177 r_ret: sys::GDExtensionVariantPtr,
1178 ) -> sys::GDExtensionBool {
1179 // SAFETY: `p_name` is a valid `StringName` pointer.
1180 let name = unsafe { StringName::new_from_string_sys(p_name) };
1181
1182 let ctx = || {
1183 format!(
1184 "error when calling {}::property_get_fallback",
1185 type_name::<T>()
1186 )
1187 };
1188
1189 let return_value = handle_panic(ctx, || {
1190 // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
1191 unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) }
1192 .borrow()
1193 .property_get_fallback(name)
1194 });
1195
1196 match return_value {
1197 Ok(Some(variant)) => {
1198 // SAFETY: It is safe to move a `Variant` into `r_ret`.
1199 unsafe { variant.move_into_var_ptr(r_ret) };
1200 SYS_TRUE
1201 }
1202 _ => SYS_FALSE,
1203 }
1204 }
1205
1206 /// # Safety
1207 ///
1208 /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
1209 /// - `p_name` must be a valid [`StringName`] pointer.
1210 /// - `p_value` must be a valid [`Variant`] pointer.
1211 pub(super) unsafe extern "C" fn set_fallback_func<T: ScriptInstance>(
1212 p_instance: sys::GDExtensionScriptInstanceDataPtr,
1213 p_name: sys::GDExtensionConstStringNamePtr,
1214 p_value: sys::GDExtensionConstVariantPtr,
1215 ) -> sys::GDExtensionBool {
1216 let (name, value);
1217 // SAFETY: `p_name` and `p_value` are valid `StringName` and `Variant` pointers respectively.
1218 unsafe {
1219 name = StringName::new_from_string_sys(p_name);
1220 value = Variant::borrow_var_sys(p_value);
1221 };
1222
1223 let ctx = || {
1224 format!(
1225 "error when calling {}::property_set_fallback",
1226 type_name::<T>()
1227 )
1228 };
1229
1230 let result = handle_panic(ctx, || {
1231 // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
1232 let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
1233 let mut guard = instance.borrow_mut();
1234
1235 let instance_guard = SiMut::new(instance.cell_ref(), &mut guard, &instance.base);
1236 ScriptInstance::property_set_fallback(instance_guard, name, value)
1237 })
1238 .unwrap_or_default();
1239
1240 bool_to_sys(result)
1241 }
1242
1243 /// # Safety
1244 ///
1245 /// - `p_instance` must point to a live immutable [`ScriptInstanceData<T>`] for the duration of this function call
1246 /// - `p_method` has to point to a valid [`StringName`].
1247 /// - `p_value` must be a valid [`sys::GDExtensionBool`] pointer.
1248 #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
1249 pub(super) unsafe extern "C" fn get_method_argument_count_func<T: ScriptInstance>(
1250 p_instance: sys::GDExtensionScriptInstanceDataPtr,
1251 p_method: sys::GDExtensionConstStringNamePtr,
1252 r_is_valid: *mut sys::GDExtensionBool,
1253 ) -> sys::GDExtensionInt {
1254 // SAFETY: `p_method` is a valid [`StringName`] pointer.
1255 let method = unsafe { StringName::new_from_string_sys(p_method) };
1256 let ctx = || {
1257 format!(
1258 "error when calling {}::get_method_argument_count_func",
1259 type_name::<T>()
1260 )
1261 };
1262
1263 let method_argument_count = handle_panic(ctx, || {
1264 // SAFETY: `p_instance` points to a live immutable `ScriptInstanceData<T>` for the duration of this call.
1265 unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) }
1266 // Can panic if the GdCell is currently mutably bound.
1267 .borrow()
1268 // This is user code and could cause a panic.
1269 .get_method_argument_count(method)
1270 })
1271 // 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.
1272 .unwrap_or_default();
1273
1274 let (result, is_valid) = match method_argument_count {
1275 Some(count) => (count, SYS_TRUE),
1276 None => (0, SYS_FALSE),
1277 };
1278
1279 // SAFETY: `r_is_valid` is assignable.
1280 unsafe { *r_is_valid = is_valid };
1281
1282 result.into()
1283 }
1284}