diff --git a/gdnative-core/Cargo.toml b/gdnative-core/Cargo.toml index 439b6aa31..4958810aa 100644 --- a/gdnative-core/Cargo.toml +++ b/gdnative-core/Cargo.toml @@ -23,6 +23,7 @@ approx = "0.4.0" glam = "0.14.0" indexmap = "1.6.0" ahash = "0.7.0" +once_cell = "1.7.2" gdnative-impl-proc-macros = { path = "../impl/proc_macros", version = "=0.9.3" } diff --git a/gdnative-core/src/nativescript/class.rs b/gdnative-core/src/nativescript/class.rs index 200c4cb98..0811b19a2 100644 --- a/gdnative-core/src/nativescript/class.rs +++ b/gdnative-core/src/nativescript/class.rs @@ -14,8 +14,8 @@ use crate::private::{get_api, ReferenceCountedClassPlaceholder}; use crate::ref_kind::{ManuallyManaged, RefCounted}; use crate::thread_access::{NonUniqueThreadAccess, Shared, ThreadAccess, ThreadLocal, Unique}; +use super::class_registry; use super::emplace; - /// Trait used for describing and initializing a Godot script class. /// /// This trait is used to provide data and functionality to the @@ -321,6 +321,8 @@ impl Instance { std::ptr::null_mut(), ); + debug_assert!(class_registry::is_class_registered::(), "`{type_name}` must be registered before it can be used; call `handle.add_class::<{type_name}>()` in your `nativescript_init` callback", type_name = std::any::type_name::()); + assert!( emplace::take::().is_none(), "emplacement value should be taken by the constructor wrapper (this is a bug in the bindings)", @@ -330,7 +332,7 @@ impl Instance { let owner = variant .try_to_object::() - .expect("base object should be of the correct type (is the script registered?)") + .expect("the engine should return a base object of the correct type") .assume_unique(); let script_ptr = diff --git a/gdnative-core/src/nativescript/class_registry.rs b/gdnative-core/src/nativescript/class_registry.rs new file mode 100644 index 000000000..1911ade28 --- /dev/null +++ b/gdnative-core/src/nativescript/class_registry.rs @@ -0,0 +1,30 @@ +use crate::nativescript::NativeClass; +use once_cell::sync::Lazy; +use parking_lot::RwLock; +use std::any::TypeId; +use std::collections::HashSet; + +static CLASS_REGISTRY: Lazy>> = Lazy::new(|| RwLock::new(HashSet::new())); + +/// Can be used to validate whether or not `C` has been added using `InitHandle::add_class()` +/// Returns true if added otherwise false. +#[inline] +pub(crate) fn is_class_registered() -> bool { + let type_id = TypeId::of::(); + CLASS_REGISTRY.read().contains(&type_id) +} + +/// Registers the class `C` in the class registry. +/// Returns `true` if `C` was not already present in the list. +/// Returns `false` if `C` was already added. +#[inline] +pub(crate) fn register_class() -> bool { + let type_id = TypeId::of::(); + CLASS_REGISTRY.write().insert(type_id) +} + +/// Clears the registry +#[inline] +pub(crate) fn cleanup() { + CLASS_REGISTRY.write().clear(); +} diff --git a/gdnative-core/src/nativescript/init.rs b/gdnative-core/src/nativescript/init.rs index e32150bc5..20f6eff36 100644 --- a/gdnative-core/src/nativescript/init.rs +++ b/gdnative-core/src/nativescript/init.rs @@ -46,8 +46,8 @@ use crate::nativescript::NativeClassMethods; use crate::nativescript::UserData; use crate::private::get_api; +use super::class_registry; use super::emplace; - pub mod method; pub mod property; @@ -96,6 +96,12 @@ impl InitHandle { where C: NativeClassMethods, { + if !class_registry::register_class::() { + panic!( + "`{type_name}` has already been registered", + type_name = std::any::type_name::() + ); + } unsafe { let class_name = CString::new(C::class_name()).unwrap(); let base_name = CString::new(C::Base::class_name()).unwrap(); diff --git a/gdnative-core/src/nativescript/mod.rs b/gdnative-core/src/nativescript/mod.rs index af8afbb0a..2eb426ec5 100644 --- a/gdnative-core/src/nativescript/mod.rs +++ b/gdnative-core/src/nativescript/mod.rs @@ -1,5 +1,6 @@ //! Types and functions related to the NativeScript extension of GDNative. +pub(crate) mod class_registry; mod emplace; mod macros; diff --git a/gdnative-core/src/private.rs b/gdnative-core/src/private.rs index a44cd6c33..46b36d592 100644 --- a/gdnative-core/src/private.rs +++ b/gdnative-core/src/private.rs @@ -109,8 +109,10 @@ pub fn get_gdnative_library_sys() -> *mut sys::godot_object { #[inline] pub unsafe fn cleanup_internal_state() { #[cfg(feature = "nativescript")] - crate::nativescript::type_tag::cleanup(); - + { + crate::nativescript::type_tag::cleanup(); + crate::nativescript::class_registry::cleanup(); + } GODOT_API = None; }