use private::Sealed;
use crate::obj::cap::GodotDefault;
use crate::obj::{Bounds, Gd, GodotClass, RawGd};
use crate::storage::{InstanceCache, Storage};
use crate::{out, sys};
pub(super) mod private {
use super::{Declarer, DynMemory, Exportable, Memory};
pub unsafe trait Bounds {
type Memory: Memory;
#[doc(hidden)]
type DynMemory: DynMemory;
type Declarer: Declarer;
#[doc(hidden)]
type Exportable: Exportable;
}
#[macro_export]
macro_rules! implement_godot_bounds {
($UserClass:ty) => {
unsafe impl $crate::obj::Bounds for $UserClass {
type Memory = <<$UserClass as $crate::obj::GodotClass>::Base as $crate::obj::Bounds>::Memory;
type DynMemory = <<$UserClass as $crate::obj::GodotClass>::Base as $crate::obj::Bounds>::DynMemory;
type Declarer = $crate::obj::bounds::DeclUser;
type Exportable = <<$UserClass as $crate::obj::GodotClass>::Base as $crate::obj::Bounds>::Exportable;
}
};
}
pub trait Sealed {}
}
pub use crate::implement_godot_bounds;
pub trait Memory: Sealed {
#[doc(hidden)]
const IS_REF_COUNTED: bool;
}
#[doc(hidden)]
pub trait DynMemory: Sealed {
#[doc(hidden)]
fn maybe_init_ref<T: GodotClass>(obj: &mut RawGd<T>);
#[doc(hidden)]
fn maybe_inc_ref<T: GodotClass>(obj: &mut RawGd<T>);
#[doc(hidden)]
unsafe fn maybe_dec_ref<T: GodotClass>(obj: &mut RawGd<T>) -> bool;
#[doc(hidden)]
fn is_ref_counted<T: GodotClass>(obj: &RawGd<T>) -> Option<bool>;
#[doc(hidden)]
fn get_ref_count<T: GodotClass>(obj: &RawGd<T>) -> Option<usize>;
#[doc(hidden)]
fn pass_as_ref(_call_type: sys::PtrcallType) -> bool {
false
}
}
pub struct MemRefCounted {}
impl Sealed for MemRefCounted {}
impl Memory for MemRefCounted {
const IS_REF_COUNTED: bool = true;
}
impl DynMemory for MemRefCounted {
fn maybe_init_ref<T: GodotClass>(obj: &mut RawGd<T>) {
out!(" MemRefc::init: {obj:?}");
if obj.is_null() {
return;
}
obj.with_ref_counted(|refc| {
let success = refc.init_ref();
assert!(success, "init_ref() failed");
});
}
fn maybe_inc_ref<T: GodotClass>(obj: &mut RawGd<T>) {
out!(" MemRefc::inc: {obj:?}");
if obj.is_null() {
return;
}
obj.with_ref_counted(|refc| {
let success = refc.reference();
assert!(success, "reference() failed");
});
}
unsafe fn maybe_dec_ref<T: GodotClass>(obj: &mut RawGd<T>) -> bool {
out!(" MemRefc::dec: {obj:?}");
if obj.is_null() {
return false;
}
obj.with_ref_counted(|refc| {
let is_last = refc.unreference();
out!(" +-- was last={is_last}");
is_last
})
}
fn is_ref_counted<T: GodotClass>(_obj: &RawGd<T>) -> Option<bool> {
Some(true)
}
fn get_ref_count<T: GodotClass>(obj: &RawGd<T>) -> Option<usize> {
let ref_count = obj.with_ref_counted(|refc| refc.get_reference_count());
Some(ref_count as usize)
}
fn pass_as_ref(call_type: sys::PtrcallType) -> bool {
matches!(call_type, sys::PtrcallType::Virtual)
}
}
#[doc(hidden)]
pub struct MemDynamic {}
impl MemDynamic {
fn inherits_refcounted<T: GodotClass>(obj: &RawGd<T>) -> bool {
obj.instance_id_unchecked()
.is_some_and(|id| id.is_ref_counted())
}
}
impl Sealed for MemDynamic {}
impl DynMemory for MemDynamic {
fn maybe_init_ref<T: GodotClass>(obj: &mut RawGd<T>) {
out!(" MemDyn::init: {obj:?}");
if Self::inherits_refcounted(obj) {
out!(" MemDyn -> MemRefc");
MemRefCounted::maybe_init_ref(obj)
} else {
out!(" MemDyn -> MemManu");
}
}
fn maybe_inc_ref<T: GodotClass>(obj: &mut RawGd<T>) {
out!(" MemDyn::inc: {obj:?}");
if Self::inherits_refcounted(obj) {
MemRefCounted::maybe_inc_ref(obj)
}
}
unsafe fn maybe_dec_ref<T: GodotClass>(obj: &mut RawGd<T>) -> bool {
unsafe {
out!(" MemDyn::dec: {obj:?}");
if obj
.instance_id_unchecked()
.is_some_and(|id| id.is_ref_counted())
{
MemRefCounted::maybe_dec_ref(obj)
} else {
false
}
}
}
fn is_ref_counted<T: GodotClass>(obj: &RawGd<T>) -> Option<bool> {
obj.instance_id_unchecked().map(|id| id.is_ref_counted())
}
fn get_ref_count<T: GodotClass>(obj: &RawGd<T>) -> Option<usize> {
if Self::inherits_refcounted(obj) {
MemRefCounted::get_ref_count(obj)
} else {
None
}
}
}
pub struct MemManual {}
impl Sealed for MemManual {}
impl Memory for MemManual {
const IS_REF_COUNTED: bool = false;
}
impl DynMemory for MemManual {
fn maybe_init_ref<T: GodotClass>(_obj: &mut RawGd<T>) {}
fn maybe_inc_ref<T: GodotClass>(_obj: &mut RawGd<T>) {}
unsafe fn maybe_dec_ref<T: GodotClass>(_obj: &mut RawGd<T>) -> bool {
false
}
fn is_ref_counted<T: GodotClass>(_obj: &RawGd<T>) -> Option<bool> {
Some(false)
}
fn get_ref_count<T: GodotClass>(_obj: &RawGd<T>) -> Option<usize> {
None
}
}
pub trait Declarer: Sealed {
#[doc(hidden)]
type DerefTarget<T: GodotClass>: GodotClass;
#[doc(hidden)]
#[allow(private_bounds)]
type InstanceCache: InstanceCache;
#[doc(hidden)]
unsafe fn is_currently_bound<T>(obj: &RawGd<T>) -> bool
where
T: GodotClass + Bounds<Declarer = Self>;
#[doc(hidden)]
fn create_gd<T>() -> Gd<T>
where
T: GodotDefault + Bounds<Declarer = Self>;
}
pub enum DeclEngine {}
impl Sealed for DeclEngine {}
impl Declarer for DeclEngine {
type DerefTarget<T: GodotClass> = T;
type InstanceCache = ();
unsafe fn is_currently_bound<T>(_obj: &RawGd<T>) -> bool
where
T: GodotClass + Bounds<Declarer = Self>,
{
false
}
fn create_gd<T>() -> Gd<T>
where
T: GodotDefault + Bounds<Declarer = Self>,
{
crate::classes::construct_engine_object()
}
}
pub enum DeclUser {}
impl Sealed for DeclUser {}
impl Declarer for DeclUser {
type DerefTarget<T: GodotClass> = T::Base;
type InstanceCache = std::cell::Cell<sys::GDExtensionClassInstancePtr>;
unsafe fn is_currently_bound<T>(obj: &RawGd<T>) -> bool
where
T: GodotClass + Bounds<Declarer = Self>,
{
unsafe { obj.storage().unwrap_unchecked().is_bound() }
}
fn create_gd<T>() -> Gd<T>
where
T: GodotDefault + Bounds<Declarer = Self>,
{
Gd::default_instance()
}
}
#[doc(hidden)]
pub trait Exportable: Sealed {}
#[doc(hidden)]
pub enum Yes {}
impl Sealed for Yes {}
impl Exportable for Yes {}
#[doc(hidden)]
pub enum No {}
impl Sealed for No {}
impl Exportable for No {}