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