use std::ffi::c_void;
use std::ops::{Deref, DerefMut};
#[cfg(feature = "experimental-threads")] #[cfg_attr(published_docs, doc(cfg(feature = "experimental-threads")))]
use godot_cell::blocking::{GdCell, MutGuard, RefGuard};
#[cfg(not(feature = "experimental-threads"))] #[cfg_attr(published_docs, doc(cfg(not(feature = "experimental-threads"))))]
use godot_cell::panicking::{GdCell, MutGuard, RefGuard};
use crate::builtin::{GString, StringName, Variant, VariantType};
use crate::classes::{Object, Script, ScriptLanguage};
use crate::meta::RawPtr;
use crate::meta::error::CallErrorType;
use crate::obj::{Base, Gd, GodotClass};
use crate::registry::info::{MethodInfo, PropertyInfo};
use crate::sys;
mod reexport_pub {
pub use crate::classes::IScriptExtension;
pub use crate::obj::Inherits;
}
pub use reexport_pub::*;
pub use crate::obj::guards::{ScriptBaseMut, ScriptBaseRef};
pub trait ScriptInstance: Sized {
type Base: GodotClass;
fn class_name(&self) -> GString;
fn set_property(this: SiMut<Self>, name: StringName, value: &Variant) -> bool;
fn get_property(&self, name: StringName) -> Option<Variant>;
fn get_property_list(&self) -> Vec<PropertyInfo>;
fn get_method_list(&self) -> Vec<MethodInfo>;
fn call(
this: SiMut<Self>,
method: StringName,
args: &[&Variant],
) -> Result<Variant, CallErrorType>;
fn is_placeholder(&self) -> bool;
fn has_method(&self, method: StringName) -> bool;
fn get_script(&self) -> &Gd<Script>;
fn get_property_type(&self, name: StringName) -> VariantType;
fn to_string(&self) -> GString;
fn get_property_state(&self) -> Vec<(StringName, Variant)>;
fn get_language(&self) -> Gd<ScriptLanguage>;
fn on_refcount_decremented(&self) -> bool;
fn on_refcount_incremented(&self);
fn property_get_fallback(&self, name: StringName) -> Option<Variant>;
fn property_set_fallback(this: SiMut<Self>, name: StringName, value: &Variant) -> bool;
#[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
fn get_method_argument_count(&self, _method: StringName) -> Option<u32>;
}
#[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
type ScriptInstanceInfo = sys::GDExtensionScriptInstanceInfo2;
#[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
type ScriptInstanceInfo = sys::GDExtensionScriptInstanceInfo3;
struct ScriptInstanceData<T: ScriptInstance> {
inner: GdCell<T>,
script_instance_ptr: *mut ScriptInstanceInfo,
#[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
property_lists: BoundedPtrList<sys::GDExtensionPropertyInfo>,
#[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
method_lists: BoundedPtrList<sys::GDExtensionMethodInfo>,
base: Base<T::Base>,
}
impl<T: ScriptInstance> std::panic::RefUnwindSafe for ScriptInstanceData<T> {}
impl<T: ScriptInstance> ScriptInstanceData<T> {
#[allow(unsafe_op_in_unsafe_fn)] unsafe fn borrow_script_sys<'a>(ptr: sys::GDExtensionScriptInstanceDataPtr) -> &'a Self {
&*(ptr.cast::<ScriptInstanceData<T>>())
}
fn borrow(&self) -> RefGuard<'_, T> {
self.inner
.borrow()
.unwrap_or_else(|err| Self::borrow_panic(err))
}
fn borrow_mut(&self) -> MutGuard<'_, T> {
self.inner
.borrow_mut()
.unwrap_or_else(|err| Self::borrow_panic(err))
}
fn cell_ref(&self) -> &GdCell<T> {
&self.inner
}
fn borrow_panic(err: Box<dyn std::error::Error>) -> ! {
panic!(
"\
ScriptInstance borrow failed, already bound; T = {}.\n \
Make sure to use `SiMut::base_mut()` when possible.\n \
Details: {err}.\
",
std::any::type_name::<T>(),
)
}
}
impl<T: ScriptInstance> Drop for ScriptInstanceData<T> {
fn drop(&mut self) {
let instance = unsafe { Box::from_raw(self.script_instance_ptr) };
drop(instance);
}
}
#[must_use]
pub unsafe fn create_script_instance<T: ScriptInstance>(
rust_instance: T,
for_object: Gd<T::Base>,
) -> RawPtr<*mut c_void> {
let gd_instance = ScriptInstanceInfo {
set_func: Some(script_instance_info::set_property_func::<T>),
get_func: Some(script_instance_info::get_property_func::<T>),
get_property_list_func: Some(script_instance_info::get_property_list_func::<T>),
#[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
free_property_list_func: Some(script_instance_info::free_property_list_func::<T>),
#[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
free_property_list_func: Some(script_instance_info::free_property_list_func),
get_class_category_func: None,
property_can_revert_func: None, property_get_revert_func: None,
get_owner_func: None,
get_property_state_func: Some(script_instance_info::get_property_state_func::<T>),
get_method_list_func: Some(script_instance_info::get_method_list_func::<T>),
#[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
free_method_list_func: Some(script_instance_info::free_method_list_func::<T>),
#[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
free_method_list_func: Some(script_instance_info::free_method_list_func),
get_property_type_func: Some(script_instance_info::get_property_type_func::<T>),
validate_property_func: None,
has_method_func: Some(script_instance_info::has_method_func::<T>),
call_func: Some(script_instance_info::call_func::<T>),
notification_func: None,
to_string_func: Some(script_instance_info::to_string_func::<T>),
refcount_incremented_func: Some(script_instance_info::refcount_incremented_func::<T>),
refcount_decremented_func: Some(script_instance_info::refcount_decremented_func::<T>),
get_script_func: Some(script_instance_info::get_script_func::<T>),
is_placeholder_func: Some(script_instance_info::is_placeholder_func::<T>),
get_fallback_func: Some(script_instance_info::get_fallback_func::<T>),
set_fallback_func: Some(script_instance_info::set_fallback_func::<T>),
get_language_func: Some(script_instance_info::get_language_func::<T>),
free_func: Some(script_instance_info::free_func::<T>),
#[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
get_method_argument_count_func: Some(
script_instance_info::get_method_argument_count_func::<T>,
),
};
let instance_ptr = Box::into_raw(Box::new(gd_instance));
let data = ScriptInstanceData {
inner: GdCell::new(rust_instance),
script_instance_ptr: instance_ptr,
#[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
property_lists: BoundedPtrList::new(),
#[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
method_lists: BoundedPtrList::new(),
base: unsafe { Base::from_script_gd(&for_object) },
};
let data_ptr = Box::into_raw(Box::new(data));
let instance_extension: sys::GDExtensionScriptInstancePtr = unsafe {
#[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
let create_fn = sys::interface_fn!(script_instance_create2);
#[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
let create_fn = sys::interface_fn!(script_instance_create3);
create_fn(
instance_ptr,
data_ptr as sys::GDExtensionScriptInstanceDataPtr,
)
};
unsafe { RawPtr::new(instance_extension.cast::<c_void>()) }
}
pub fn script_instance_exists<O, S>(object: &Gd<O>, script: &Gd<S>) -> bool
where
O: Inherits<Object>,
S: Inherits<Script> + IScriptExtension + super::Bounds<Declarer = super::bounds::DeclUser>,
{
let Some(object_script) = object.upcast_ref().get_script() else {
return false;
};
if object_script.instance_id() != script.instance_id() {
return false;
}
let Some(language) = script.bind().get_language() else {
return false;
};
let get_instance_fn = sys::interface_fn!(object_get_script_instance);
let instance = unsafe { get_instance_fn(object.obj_sys(), language.obj_sys()) };
!instance.is_null()
}
pub struct SiMut<'a, T: ScriptInstance> {
mut_ref: &'a mut T,
cell: &'a GdCell<T>,
base_ref: &'a Base<T::Base>,
}
impl<'a, T: ScriptInstance> SiMut<'a, T> {
fn new(
cell: &'a GdCell<T>,
cell_guard: &'a mut MutGuard<T>,
base_ref: &'a Base<T::Base>,
) -> Self {
let mut_ref = cell_guard.deref_mut();
Self {
mut_ref,
cell,
base_ref,
}
}
pub fn base(&self) -> ScriptBaseRef<'_, T> {
let passive_gd = self.base_ref.to_script_passive();
ScriptBaseRef::new(passive_gd, self.mut_ref)
}
pub fn base_mut(&mut self) -> ScriptBaseMut<'_, T> {
let guard = self.cell.make_inaccessible(self.mut_ref).unwrap();
let passive_gd = self.base_ref.to_script_passive();
ScriptBaseMut::new(passive_gd, guard)
}
}
impl<T: ScriptInstance> Deref for SiMut<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.mut_ref
}
}
impl<T: ScriptInstance> DerefMut for SiMut<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.mut_ref
}
}
#[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
mod bounded_ptr_list {
use std::collections::HashMap;
use std::sync::Mutex;
use godot_ffi as sys;
pub struct BoundedPtrList<T> {
list_lengths: Mutex<HashMap<*mut T, u32>>,
}
impl<T> BoundedPtrList<T> {
pub fn new() -> Self {
Self {
list_lengths: Mutex::new(HashMap::new()),
}
}
pub fn list_into_sys(&self, list: Vec<T>) -> (*const T, u32) {
let len: u32 = list
.len()
.try_into()
.expect("list must have length that fits in u32");
let ptr = Box::leak(list.into_boxed_slice()).as_mut_ptr();
let old_value = self.list_lengths.lock().unwrap().insert(ptr, len);
assert_eq!(
old_value, None,
"attempted to insert the same list twice, this is a bug"
);
(ptr.cast_const(), len)
}
pub unsafe fn list_from_sys(&self, ptr: *const T) -> Box<[T]> {
let ptr: *mut T = ptr.cast_mut();
let len = self
.list_lengths
.lock()
.unwrap()
.remove(&ptr)
.expect("attempted to free list from wrong collection, this is a bug");
let len: usize = sys::conv::u32_to_usize(len);
let slice = unsafe { std::slice::from_raw_parts_mut(ptr, len) };
unsafe { Box::from_raw(slice) }
}
}
}
#[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
use self::bounded_ptr_list::BoundedPtrList;
mod script_instance_info {
use std::any::type_name;
use std::ffi::c_void;
use sys::conv::{SYS_FALSE, SYS_TRUE, bool_to_sys};
#[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
use sys::conv::{ptr_list_from_sys, ptr_list_into_sys};
use super::{ScriptInstance, ScriptInstanceData, SiMut};
use crate::builtin::{StringName, Variant};
use crate::private::{PanicPayload, handle_panic};
use crate::registry::info::{MethodInfo, PropertyInfo};
use crate::sys;
pub(super) unsafe extern "C" fn set_property_func<T: ScriptInstance>(
p_instance: sys::GDExtensionScriptInstanceDataPtr,
p_name: sys::GDExtensionConstStringNamePtr,
p_value: sys::GDExtensionConstVariantPtr,
) -> sys::GDExtensionBool {
let (instance, name, value);
unsafe {
instance = ScriptInstanceData::<T>::borrow_script_sys(p_instance);
name = StringName::new_from_string_sys(p_name);
value = Variant::borrow_var_sys(p_value);
}
let result = with_instance_mut(instance, "set", |i| {
ScriptInstance::set_property(i, name, value)
})
.unwrap_or_default();
bool_to_sys(result)
}
pub(super) unsafe extern "C" fn get_property_func<T: ScriptInstance>(
p_instance: sys::GDExtensionScriptInstanceDataPtr,
p_name: sys::GDExtensionConstStringNamePtr,
r_ret: sys::GDExtensionVariantPtr,
) -> sys::GDExtensionBool {
let (instance, name);
unsafe {
instance = ScriptInstanceData::<T>::borrow_script_sys(p_instance);
name = StringName::new_from_string_sys(p_name);
}
let return_value = with_instance(instance, "get", |i| i.get_property(name));
match return_value {
Ok(Some(variant)) => {
unsafe { variant.move_into_var_ptr(r_ret) };
SYS_TRUE
}
_ => SYS_FALSE,
}
}
pub(super) unsafe extern "C" fn get_property_list_func<T: ScriptInstance>(
p_instance: sys::GDExtensionScriptInstanceDataPtr,
r_count: *mut u32,
) -> *const sys::GDExtensionPropertyInfo {
let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
let property_list = with_instance(instance, "get_property_list", |i| {
i.get_property_list()
.into_iter()
.map(|prop| prop.into_owned_property_sys())
.collect::<Vec<_>>()
})
.unwrap_or_default();
#[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
let (list_ptr, list_length) = instance.property_lists.list_into_sys(property_list);
#[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
let (list_ptr, list_length) = ptr_list_into_sys(property_list);
unsafe {
*r_count = list_length;
}
list_ptr
}
pub(super) unsafe extern "C" fn get_method_list_func<T: ScriptInstance>(
p_instance: sys::GDExtensionScriptInstanceDataPtr,
r_count: *mut u32,
) -> *const sys::GDExtensionMethodInfo {
let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
let method_list = with_instance(instance, "get_method_list", |i| {
i.get_method_list()
.into_iter()
.map(|method| method.into_owned_method_sys())
.collect()
})
.unwrap_or_default();
#[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
let (return_pointer, list_length) = instance.method_lists.list_into_sys(method_list);
#[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
let (return_pointer, list_length) = ptr_list_into_sys(method_list);
unsafe {
*r_count = list_length;
}
return_pointer
}
#[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
pub(super) unsafe extern "C" fn free_property_list_func<T: ScriptInstance>(
p_instance: sys::GDExtensionScriptInstanceDataPtr,
p_prop_info: *const sys::GDExtensionPropertyInfo,
) {
let (instance, property_infos);
unsafe {
instance = ScriptInstanceData::<T>::borrow_script_sys(p_instance);
property_infos = instance.property_lists.list_from_sys(p_prop_info);
}
for info in property_infos.iter() {
unsafe { PropertyInfo::free_owned_property_sys(*info) };
}
}
#[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
pub(super) unsafe extern "C" fn free_property_list_func(
_p_instance: sys::GDExtensionScriptInstanceDataPtr,
p_prop_info: *const sys::GDExtensionPropertyInfo,
p_len: u32,
) {
let property_infos = unsafe { ptr_list_from_sys(p_prop_info, p_len) };
for info in property_infos.iter() {
unsafe { PropertyInfo::free_owned_property_sys(*info) };
}
}
pub(super) unsafe extern "C" fn call_func<T: ScriptInstance>(
p_self: sys::GDExtensionScriptInstanceDataPtr,
p_method: sys::GDExtensionConstStringNamePtr,
p_args: *const sys::GDExtensionConstVariantPtr,
p_argument_count: sys::GDExtensionInt,
r_return: sys::GDExtensionVariantPtr,
r_error: *mut sys::GDExtensionCallError,
) {
let (instance, method, args);
unsafe {
instance = ScriptInstanceData::<T>::borrow_script_sys(p_self);
method = StringName::new_from_string_sys(p_method);
args = Variant::borrow_ref_slice(
p_args,
p_argument_count
.try_into()
.expect("argument count should be a valid `u32`"),
);
}
let result = with_instance_mut(instance, "call", |i| {
ScriptInstance::call(i, method.clone(), args)
});
let error = match result {
Ok(Ok(variant)) => {
unsafe { variant.move_into_var_ptr(r_return) };
sys::GDEXTENSION_CALL_OK
}
Ok(Err(err)) => err.to_sys(),
Err(_) => sys::GDEXTENSION_CALL_ERROR_INVALID_METHOD,
};
unsafe { (*r_error).error = error };
}
pub(super) unsafe extern "C" fn get_script_func<T: ScriptInstance>(
p_instance: sys::GDExtensionScriptInstanceDataPtr,
) -> sys::GDExtensionObjectPtr {
let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
let script = with_instance(instance, "get_script", |i| i.get_script().clone());
match script {
Ok(script) => script.obj_sys(),
Err(_) => std::ptr::null_mut(),
}
}
pub(super) unsafe extern "C" fn is_placeholder_func<T: ScriptInstance>(
p_instance: sys::GDExtensionScriptInstanceDataPtr,
) -> sys::GDExtensionBool {
let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
let is_placeholder =
with_instance(instance, "is_placeholder", |i| i.is_placeholder()).unwrap_or_default();
bool_to_sys(is_placeholder)
}
pub(super) unsafe extern "C" fn has_method_func<T: ScriptInstance>(
p_instance: sys::GDExtensionScriptInstanceDataPtr,
p_method: sys::GDExtensionConstStringNamePtr,
) -> sys::GDExtensionBool {
let (instance, method);
unsafe {
instance = ScriptInstanceData::<T>::borrow_script_sys(p_instance);
method = StringName::new_from_string_sys(p_method);
}
let has_method =
with_instance(instance, "has_method", |i| i.has_method(method)).unwrap_or_default();
bool_to_sys(has_method)
}
#[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
pub(super) unsafe extern "C" fn free_method_list_func<T: ScriptInstance>(
p_instance: sys::GDExtensionScriptInstanceDataPtr,
p_method_info: *const sys::GDExtensionMethodInfo,
) {
let (instance, method_infos);
unsafe {
instance = ScriptInstanceData::<T>::borrow_script_sys(p_instance);
method_infos = instance.method_lists.list_from_sys(p_method_info);
}
for info in method_infos.iter() {
unsafe { MethodInfo::free_owned_method_sys(*info) };
}
}
#[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
pub(super) unsafe extern "C" fn free_method_list_func(
_p_instance: sys::GDExtensionScriptInstanceDataPtr,
p_method_info: *const sys::GDExtensionMethodInfo,
p_len: u32,
) {
let method_infos = unsafe { ptr_list_from_sys(p_method_info, p_len) };
for info in method_infos.iter() {
unsafe { MethodInfo::free_owned_method_sys(*info) };
}
}
pub(super) unsafe extern "C" fn get_property_type_func<T: ScriptInstance>(
p_instance: sys::GDExtensionScriptInstanceDataPtr,
p_name: sys::GDExtensionConstStringNamePtr,
r_is_valid: *mut sys::GDExtensionBool,
) -> sys::GDExtensionVariantType {
let (instance, name);
unsafe {
instance = ScriptInstanceData::<T>::borrow_script_sys(p_instance);
name = StringName::new_from_string_sys(p_name);
}
let result = with_instance(instance, "get_property_type", |i| {
i.get_property_type(name.clone())
});
let (is_valid, result) = if let Ok(result) = result {
(SYS_TRUE, result.sys())
} else {
(SYS_FALSE, sys::GDEXTENSION_VARIANT_TYPE_NIL)
};
unsafe { *r_is_valid = is_valid };
result
}
pub(super) unsafe extern "C" fn to_string_func<T: ScriptInstance>(
p_instance: sys::GDExtensionScriptInstanceDataPtr,
r_is_valid: *mut sys::GDExtensionBool,
r_str: sys::GDExtensionStringPtr,
) {
let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
let string = with_instance(instance, "to_string", |i| i.to_string()).ok();
let Some(string) = string else {
return;
};
unsafe { *r_is_valid = SYS_TRUE };
unsafe { string.move_into_string_ptr(r_str) };
}
pub(super) unsafe extern "C" fn get_property_state_func<T: ScriptInstance>(
p_instance: sys::GDExtensionScriptInstanceDataPtr,
property_state_add: sys::GDExtensionScriptInstancePropertyStateAdd,
userdata: *mut c_void,
) {
let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
let property_states =
with_instance(instance, "get_property_state", |i| i.get_property_state())
.unwrap_or_default();
let Some(property_state_add) = property_state_add else {
return;
};
for (name, value) in property_states {
unsafe {
property_state_add(
name.into_owned_string_sys(),
value.into_owned_var_sys(),
userdata,
)
}
}
}
pub(super) unsafe extern "C" fn get_language_func<T: ScriptInstance>(
p_instance: sys::GDExtensionScriptInstanceDataPtr,
) -> sys::GDExtensionScriptLanguagePtr {
let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
let language = with_instance(instance, "get_language", |i| i.get_language());
if let Ok(language) = language {
language.obj_sys().cast()
} else {
std::ptr::null_mut()
}
}
pub(super) unsafe extern "C" fn free_func<T: ScriptInstance>(
p_instance: sys::GDExtensionScriptInstanceDataPtr,
) {
unsafe { drop(Box::from_raw(p_instance.cast::<ScriptInstanceData<T>>())) }
}
pub(super) unsafe extern "C" fn refcount_decremented_func<T: ScriptInstance>(
p_instance: sys::GDExtensionScriptInstanceDataPtr,
) -> sys::GDExtensionBool {
let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
let result = with_instance(instance, "refcount_decremented", |i| {
i.on_refcount_decremented()
})
.unwrap_or(true);
bool_to_sys(result)
}
pub(super) unsafe extern "C" fn refcount_incremented_func<T: ScriptInstance>(
p_instance: sys::GDExtensionScriptInstanceDataPtr,
) {
let instance = unsafe { ScriptInstanceData::<T>::borrow_script_sys(p_instance) };
with_instance(instance, "refcount_incremented", |i| {
i.on_refcount_incremented()
})
.unwrap_or_default();
}
pub(super) unsafe extern "C" fn get_fallback_func<T: ScriptInstance>(
p_instance: sys::GDExtensionScriptInstanceDataPtr,
p_name: sys::GDExtensionConstStringNamePtr,
r_ret: sys::GDExtensionVariantPtr,
) -> sys::GDExtensionBool {
let (instance, name);
unsafe {
instance = ScriptInstanceData::<T>::borrow_script_sys(p_instance);
name = StringName::new_from_string_sys(p_name);
}
let return_value = with_instance(instance, "property_get_fallback", |i| {
i.property_get_fallback(name)
});
match return_value {
Ok(Some(variant)) => {
unsafe { variant.move_into_var_ptr(r_ret) };
SYS_TRUE
}
_ => SYS_FALSE,
}
}
pub(super) unsafe extern "C" fn set_fallback_func<T: ScriptInstance>(
p_instance: sys::GDExtensionScriptInstanceDataPtr,
p_name: sys::GDExtensionConstStringNamePtr,
p_value: sys::GDExtensionConstVariantPtr,
) -> sys::GDExtensionBool {
let (instance, name, value);
unsafe {
instance = ScriptInstanceData::<T>::borrow_script_sys(p_instance);
name = StringName::new_from_string_sys(p_name);
value = Variant::borrow_var_sys(p_value);
}
let result = with_instance_mut(instance, "property_set_fallback", |i| {
ScriptInstance::property_set_fallback(i, name, value)
})
.unwrap_or_default();
bool_to_sys(result)
}
#[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
pub(super) unsafe extern "C" fn get_method_argument_count_func<T: ScriptInstance>(
p_instance: sys::GDExtensionScriptInstanceDataPtr,
p_method: sys::GDExtensionConstStringNamePtr,
r_is_valid: *mut sys::GDExtensionBool,
) -> sys::GDExtensionInt {
let (instance, method);
unsafe {
instance = ScriptInstanceData::<T>::borrow_script_sys(p_instance);
method = StringName::new_from_string_sys(p_method);
}
let method_argument_count = with_instance(instance, "get_method_argument_count", |i| {
i.get_method_argument_count(method)
})
.unwrap_or_default();
let (result, is_valid) = match method_argument_count {
Some(count) => (count, SYS_TRUE),
None => (0, SYS_FALSE),
};
unsafe { *r_is_valid = is_valid };
result.into()
}
fn error_ctx<T: ScriptInstance>(method: &'static str) -> impl Fn() -> String {
move || format!("{}::{method}(), script instance method", type_name::<T>())
}
fn with_instance<T: ScriptInstance, R>(
instance: &ScriptInstanceData<T>,
method: &'static str,
f: impl FnOnce(&T) -> R + std::panic::UnwindSafe,
) -> Result<R, PanicPayload> {
handle_panic(error_ctx::<T>(method), || f(&instance.borrow()))
}
fn with_instance_mut<T: ScriptInstance, R>(
instance: &ScriptInstanceData<T>,
method: &'static str,
f: impl FnOnce(SiMut<T>) -> R + std::panic::UnwindSafe,
) -> Result<R, PanicPayload> {
handle_panic(error_ctx::<T>(method), || {
let mut guard = instance.borrow_mut();
let instance_guard = SiMut::new(instance.cell_ref(), &mut guard, &instance.base);
f(instance_guard)
})
}
}