use std::cell::Cell;
use std::ops::{Deref, DerefMut};
use std::ptr;
#[cfg(feature = "experimental-threads")] #[cfg_attr(published_docs, doc(cfg(feature = "experimental-threads")))]
use godot_cell::blocking::{InaccessibleGuard, MutGuard, RefGuard};
#[cfg(not(feature = "experimental-threads"))] #[cfg_attr(published_docs, doc(cfg(not(feature = "experimental-threads"))))]
use godot_cell::panicking::{InaccessibleGuard, MutGuard, RefGuard};
use godot_ffi as sys;
use crate::godot_error;
use crate::obj::{Base, Gd, GodotClass, Inherits, Singleton};
use crate::storage::log_pre_drop;
#[derive(Copy, Clone, Debug)]
pub enum Lifecycle {
Alive,
Destroying,
}
#[cfg_attr(not(feature = "experimental-threads"), allow(dead_code))]
pub struct AtomicLifecycle {
atomic: std::sync::atomic::AtomicU32,
}
#[cfg_attr(not(feature = "experimental-threads"), allow(dead_code))]
impl AtomicLifecycle {
pub fn new(value: Lifecycle) -> Self {
Self {
atomic: std::sync::atomic::AtomicU32::new(value as u32),
}
}
pub fn get(&self) -> Lifecycle {
match self.atomic.load(std::sync::atomic::Ordering::Relaxed) {
0 => Lifecycle::Alive,
1 => Lifecycle::Destroying,
other => panic!("invalid lifecycle {other}"),
}
}
pub fn set(&self, lifecycle: Lifecycle) {
let value = match lifecycle {
Lifecycle::Alive => 0,
Lifecycle::Destroying => 1,
};
self.atomic
.store(value, std::sync::atomic::Ordering::Relaxed);
}
}
pub unsafe trait Storage {
type Instance: GodotClass;
fn construct(
user_instance: Self::Instance,
base: Base<<Self::Instance as GodotClass>::Base>,
) -> Self;
fn is_bound(&self) -> bool;
fn base(&self) -> &Base<<Self::Instance as GodotClass>::Base>;
fn get(&self) -> RefGuard<'_, Self::Instance>;
fn get_mut(&self) -> MutGuard<'_, Self::Instance>;
fn get_inaccessible<'a: 'b, 'b>(
&'a self,
instance: &'b mut Self::Instance,
) -> InaccessibleGuard<'b, Self::Instance>;
fn get_lifecycle(&self) -> Lifecycle;
fn set_lifecycle(&self, lifecycle: Lifecycle);
fn get_gd(&self) -> Gd<Self::Instance>
where
Self::Instance: Inherits<<Self::Instance as GodotClass>::Base>,
{
self.base().__constructed_gd().cast()
}
#[must_use]
fn into_raw(self) -> *mut Self
where
Self: Sized,
{
Box::into_raw(Box::new(self))
}
fn mark_destroyed_by_godot(&self) {
self.set_lifecycle(Lifecycle::Destroying);
log_pre_drop(self);
}
}
pub(crate) trait StorageRefCounted: Storage {
fn on_inc_ref(&self);
fn on_dec_ref(&self);
}
#[cfg(not(feature = "experimental-threads"))] #[cfg_attr(published_docs, doc(cfg(not(feature = "experimental-threads"))))]
pub type InstanceStorage<T> = crate::storage::single_threaded::InstanceStorage<T>;
#[cfg(feature = "experimental-threads")] #[cfg_attr(published_docs, doc(cfg(feature = "experimental-threads")))]
pub type InstanceStorage<T> = crate::storage::multi_threaded::InstanceStorage<T>;
const fn _assert_implements_storage<T: Storage + StorageRefCounted>() {}
const _INSTANCE_STORAGE_IMPLEMENTS_STORAGE: () =
_assert_implements_storage::<InstanceStorage<crate::classes::Object>>();
#[doc(hidden)]
pub struct VirtualMethodReceiver<'a, T: GodotClass> {
inner: VirtualMethodReceiverInner<'a, T>,
}
enum VirtualMethodReceiverInner<'a, T: GodotClass> {
Ref(RefGuard<'a, T>),
Mut(MutGuard<'a, T>),
GdSelf(Gd<T>),
Uninit,
}
impl<'a, T: GodotClass> VirtualMethodReceiver<'a, T> {
pub fn recv_gd(mut self) -> Gd<T> {
match std::mem::replace(&mut self.inner, VirtualMethodReceiverInner::Uninit) {
VirtualMethodReceiverInner::GdSelf(instance) => instance,
_ => panic!("Tried to use Gd<Self> receiver for method which doesn't accept it."),
}
}
pub fn recv_self(mut self) -> impl Deref<Target = T> + use<'a, T> {
match std::mem::replace(&mut self.inner, VirtualMethodReceiverInner::Uninit) {
VirtualMethodReceiverInner::Ref(instance) => instance,
_ => panic!("Tried to use &self receiver for method which doesn't accept it."),
}
}
pub fn recv_self_mut(mut self) -> impl DerefMut<Target = T> + use<'a, T> {
match std::mem::replace(&mut self.inner, VirtualMethodReceiverInner::Uninit) {
VirtualMethodReceiverInner::Mut(instance) => instance,
_ => panic!("Tried to use &mut self receiver for method which doesn't accept it."),
}
}
}
#[doc(hidden)]
pub enum RecvRef {}
#[doc(hidden)]
pub enum RecvMut {}
#[doc(hidden)]
pub enum RecvGdSelf {}
#[doc(hidden)]
pub trait IntoVirtualMethodReceiver<T: GodotClass> {
#[doc(hidden)]
fn instance<'a, 'b: 'a>(storage: &'b InstanceStorage<T>) -> VirtualMethodReceiver<'a, T>;
}
impl<T: GodotClass> IntoVirtualMethodReceiver<T> for RecvRef {
fn instance<'a, 'b: 'a>(storage: &'b InstanceStorage<T>) -> VirtualMethodReceiver<'a, T> {
VirtualMethodReceiver {
inner: VirtualMethodReceiverInner::Ref(storage.get()),
}
}
}
impl<T: GodotClass> IntoVirtualMethodReceiver<T> for RecvMut {
fn instance<'a, 'b: 'a>(storage: &'b InstanceStorage<T>) -> VirtualMethodReceiver<'a, T> {
VirtualMethodReceiver {
inner: VirtualMethodReceiverInner::Mut(storage.get_mut()),
}
}
}
impl<T> IntoVirtualMethodReceiver<T> for RecvGdSelf
where
T: Inherits<<T as GodotClass>::Base>,
{
fn instance<'a, 'b: 'a>(storage: &'b InstanceStorage<T>) -> VirtualMethodReceiver<'a, T> {
VirtualMethodReceiver {
inner: VirtualMethodReceiverInner::GdSelf(storage.get_gd()),
}
}
}
pub unsafe fn as_storage<'u, T: GodotClass>(
instance_ptr: sys::GDExtensionClassInstancePtr,
) -> &'u InstanceStorage<T> {
unsafe { &*(instance_ptr as *mut InstanceStorage<T>) }
}
pub unsafe fn destroy_storage<T: GodotClass>(instance_ptr: sys::GDExtensionClassInstancePtr) {
let raw = instance_ptr as *mut InstanceStorage<T>;
let storage = unsafe { &mut *raw };
let mut leak_rust_object = false;
if storage.is_bound() {
let error = format!(
"Destroyed an object from Godot side, while a bind() or bind_mut() call was active.\n \
This is a bug in your code that may cause UB and logic errors. Make sure that objects are not\n \
destroyed while you still hold a Rust reference to them, or use Gd::free() which is safe.\n \
object: {:?}",
storage.base()
);
if cfg!(safeguards_balanced) {
let error = crate::builtin::GString::from(&error);
crate::classes::Os::singleton().crash(&error);
} else {
leak_rust_object = true;
godot_error!("{}", error);
}
}
if !leak_rust_object {
let _drop = unsafe { Box::from_raw(storage) };
}
}
pub(crate) trait InstanceCache: Clone {
fn null() -> Self;
}
impl InstanceCache for () {
fn null() -> Self {} }
impl InstanceCache for Cell<sys::GDExtensionClassInstancePtr> {
fn null() -> Self {
Cell::new(ptr::null_mut())
}
}
pub fn nop_instance_callbacks() -> sys::GDExtensionInstanceBindingCallbacks {
sys::GDExtensionInstanceBindingCallbacks {
create_callback: Some(create_callback),
free_callback: Some(free_callback),
reference_callback: Some(reference_callback),
}
}
extern "C" fn create_callback(
_p_token: *mut std::os::raw::c_void,
_p_instance: *mut std::os::raw::c_void,
) -> *mut std::os::raw::c_void {
ptr::null_mut()
}
extern "C" fn free_callback(
_p_token: *mut std::os::raw::c_void,
_p_instance: *mut std::os::raw::c_void,
_p_binding: *mut std::os::raw::c_void,
) {
}
extern "C" fn reference_callback(
_p_token: *mut std::os::raw::c_void,
_p_binding: *mut std::os::raw::c_void,
_p_reference: sys::GDExtensionBool,
) -> sys::GDExtensionBool {
true as u8
}