mod instance_storage;
#[cfg_attr(not(feature = "experimental-threads"), allow(dead_code))]
mod multi_threaded;
#[cfg_attr(feature = "experimental-threads", allow(dead_code))]
mod single_threaded;
use std::any::type_name;
pub use instance_storage::*;
fn bind_failed<T>(err: Box<dyn std::error::Error>, tracker: &DebugBorrowTracker) -> ! {
let ty = type_name::<T>();
eprint!("{tracker}");
panic!(
"Gd<T>::bind() failed, already bound; T = {ty}.\n \
Make sure to use `self.base_mut()` or `self.base()` instead of `self.to_gd()` when possible.\n \
Details: {err}."
)
}
fn bind_mut_failed<T>(err: Box<dyn std::error::Error>, tracker: &DebugBorrowTracker) -> ! {
let ty = type_name::<T>();
eprint!("{tracker}");
panic!(
"Gd<T>::bind_mut() failed, already bound; T = {ty}.\n \
Make sure to use `self.base_mut()` instead of `self.to_gd()` when possible.\n \
Details: {err}."
)
}
fn bug_inaccessible<T>(err: Box<dyn std::error::Error>) -> ! {
let ty = type_name::<T>();
panic!(
"`base_mut()` failed for type T = {ty}.\n \
This is most likely a bug, please report it.\n \
Details: {err}."
)
}
#[cfg(feature = "debug-log")] #[cfg_attr(published_docs, doc(cfg(feature = "debug-log")))]
use log_active::*;
#[cfg(not(feature = "debug-log"))] #[cfg_attr(published_docs, doc(cfg(not(feature = "debug-log"))))]
use log_inactive::*;
#[cfg(feature = "debug-log")] #[cfg_attr(published_docs, doc(cfg(feature = "debug-log")))]
mod log_active {
use godot_ffi::out;
use super::*;
pub fn log_construct<T: GodotClass>(base: &Base<T::Base>) {
out!(
" Storage::construct: {base:?} (T={ty})",
ty = type_name::<T>()
);
}
pub fn log_inc_ref<T: StorageRefCounted>(storage: &T) {
out!(
" Storage::on_inc_ref: {base:?} (T={ty})",
base = storage.base(),
ty = type_name::<T>(),
);
}
pub fn log_dec_ref<T: StorageRefCounted>(storage: &T) {
out!(
" | Storage::on_dec_ref: {base:?} (T={ty})",
base = storage.base(),
ty = type_name::<T>(),
);
}
pub fn log_pre_drop<T: Storage + ?Sized>(storage: &T) {
out!(
" Storage::mark_destroyed_by_godot: {base_id} (lcy={lifecycle:?})",
base_id = storage.base().debug_instance_id(),
lifecycle = storage.get_lifecycle(),
);
}
pub fn log_drop<T: StorageRefCounted>(storage: &T) {
out!(
" Storage::drop: {base_id}",
base_id = storage.base().debug_instance_id(),
);
}
}
#[cfg(not(feature = "debug-log"))] #[cfg_attr(published_docs, doc(cfg(not(feature = "debug-log"))))]
mod log_inactive {
use super::*;
pub fn log_construct<T: GodotClass>(_base: &Base<T::Base>) {}
pub fn log_inc_ref<T: StorageRefCounted>(_storage: &T) {}
pub fn log_dec_ref<T: StorageRefCounted>(_storage: &T) {}
pub fn log_pre_drop<T: Storage + ?Sized>(_storage: &T) {}
pub fn log_drop<T: StorageRefCounted>(_storage: &T) {}
}
#[cfg(safeguards_strict)] #[cfg_attr(published_docs, doc(cfg(safeguards_strict)))]
use borrow_info::DebugBorrowTracker;
#[cfg(not(safeguards_strict))] #[cfg_attr(published_docs, doc(cfg(not(safeguards_strict))))]
use borrow_info_noop::DebugBorrowTracker;
use crate::obj::{Base, GodotClass};
#[cfg(safeguards_strict)] #[cfg_attr(published_docs, doc(cfg(safeguards_strict)))]
mod borrow_info {
use std::backtrace::Backtrace;
use std::fmt;
use std::sync::Mutex;
struct TrackedBorrow {
backtrace: Backtrace,
is_mut: bool,
}
pub(super) struct DebugBorrowTracker {
last_borrow: Mutex<Option<TrackedBorrow>>,
}
impl DebugBorrowTracker {
pub fn new() -> Self {
Self {
last_borrow: Mutex::new(None),
}
}
#[track_caller]
pub fn track_ref_borrow(&self) {
let mut guard = self.last_borrow.lock().unwrap();
*guard = Some(TrackedBorrow {
backtrace: Backtrace::capture(),
is_mut: false,
});
}
#[track_caller]
pub fn track_mut_borrow(&self) {
let mut guard = self.last_borrow.lock().unwrap();
*guard = Some(TrackedBorrow {
backtrace: Backtrace::capture(),
is_mut: true,
});
}
}
impl fmt::Display for DebugBorrowTracker {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let guard = self.last_borrow.lock().unwrap();
if let Some(borrow) = &*guard {
let mutability = if borrow.is_mut { "bind_mut" } else { "bind" };
let prefix = format!("backtrace of previous `{mutability}` borrow");
let backtrace = crate::format_backtrace!(prefix, &borrow.backtrace);
writeln!(f, "{backtrace}")
} else {
writeln!(f, "no previous borrows tracked.")
}
}
}
}
#[cfg(not(safeguards_strict))] #[cfg_attr(published_docs, doc(cfg(not(safeguards_strict))))]
mod borrow_info_noop {
use std::fmt;
pub(super) struct DebugBorrowTracker;
impl DebugBorrowTracker {
pub fn new() -> Self {
Self
}
pub fn track_ref_borrow(&self) {}
pub fn track_mut_borrow(&self) {}
}
impl fmt::Display for DebugBorrowTracker {
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
Ok(())
}
}
}