Skip to content

Commit 92099e0

Browse files
committed
Document runtime class in editor requirement.
- Explicitly declare type while `cast`ing, so type inference won't try anything funny - Make `resolve_instance_ptr` safe. Returning (and casting) null ptrs is totally safe.
1 parent d1874f3 commit 92099e0

File tree

3 files changed

+53
-11
lines changed

3 files changed

+53
-11
lines changed

godot-core/src/classes/class_runtime.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,30 @@ pub(crate) fn ensure_object_inherits(derived: ClassName, base: ClassName, instan
102102
)
103103
}
104104

105+
#[cfg(debug_assertions)]
106+
pub(crate) fn ensure_binding_not_null<T>(binding: sys::GDExtensionClassInstancePtr)
107+
where
108+
T: GodotClass + Bounds<Declarer = bounds::DeclUser>,
109+
{
110+
if !binding.is_null() {
111+
return;
112+
}
113+
114+
// Non-tool classes can't be instantiated in the editor.
115+
if crate::classes::Engine::singleton().is_editor_hint() {
116+
panic!(
117+
"Class {} -- null instance; does the class have a Godot creator function? \
118+
Ensure that the given class is a tool class with #[class(tool)], if it is being accessed in the editor.",
119+
std::any::type_name::<T>()
120+
)
121+
} else {
122+
panic!(
123+
"Class {} -- null instance; does the class have a Godot creator function?",
124+
std::any::type_name::<T>()
125+
);
126+
}
127+
}
128+
105129
// ----------------------------------------------------------------------------------------------------------------------------------------------
106130
// Implementation of this file
107131

godot-core/src/obj/raw_gd.rs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -425,8 +425,19 @@ where
425425
}
426426
}
427427

428-
// TODO: document unsafety in this function, and double check that it actually needs to be unsafe.
429-
unsafe fn resolve_instance_ptr(&self) -> sys::GDExtensionClassInstancePtr {
428+
/// Retrieves and caches pointer to this class instance if `self.obj` is non-null.
429+
/// Returns a null pointer otherwise.
430+
///
431+
/// Note: The returned pointer to the GDExtensionClass instance (even when `self.obj` is non-null)
432+
/// might still be null when:
433+
/// - The class isn't instantiable in the current context.
434+
/// - The instance is a placeholder (e.g., non-`tool` classes in the editor).
435+
///
436+
/// However, null pointers might also occur in other, undocumented contexts.
437+
///
438+
/// # Panics
439+
/// In Debug mode, if binding is null.
440+
fn resolve_instance_ptr(&self) -> sys::GDExtensionClassInstancePtr {
430441
if self.is_null() {
431442
return ptr::null_mut();
432443
}
@@ -437,16 +448,21 @@ where
437448
}
438449

439450
let callbacks = crate::storage::nop_instance_callbacks();
440-
let token = sys::get_library() as *mut std::ffi::c_void;
441-
let binding = interface_fn!(object_get_instance_binding)(self.obj_sys(), token, &callbacks);
442451

443-
debug_assert!(
444-
!binding.is_null(),
445-
"Class {} -- null instance; does the class have a Godot creator function?",
446-
std::any::type_name::<T>()
447-
);
452+
// SAFETY: library is already initialized.
453+
let token = unsafe { sys::get_library() };
454+
let token = token.cast::<std::ffi::c_void>();
455+
456+
// SAFETY: ensured that `self.obj` is non-null and valid.
457+
let binding = unsafe {
458+
interface_fn!(object_get_instance_binding)(self.obj_sys(), token, &callbacks)
459+
};
460+
461+
let ptr: sys::GDExtensionClassInstancePtr = binding.cast();
462+
463+
#[cfg(debug_assertions)]
464+
crate::classes::ensure_binding_not_null::<T>(ptr);
448465

449-
let ptr = binding as sys::GDExtensionClassInstancePtr;
450466
self.cached_storage_ptr.set(ptr);
451467
ptr
452468
}

godot-macros/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,9 @@ use crate::util::{bail, ident, KvParser};
325325
/// See [`ExtensionLibrary::editor_run_behavior()`](../init/trait.ExtensionLibrary.html#method.editor_run_behavior)
326326
/// for more information and further customization.
327327
///
328-
/// This is very similar to [GDScript's `@tool` feature](https://docs.godotengine.org/en/stable/tutorials/plugins/running_code_in_the_editor.html).
328+
/// This behaves similarly to [GDScript's `@tool` feature](https://docs.godotengine.org/en/stable/tutorials/plugins/running_code_in_the_editor.html).
329+
///
330+
/// **Note**: As in GDScript, the class must be marked as a `tool` to be accessible in the editor (e.g., for use by editor plugins and inspectors).
329331
///
330332
/// ## Editor plugins
331333
///

0 commit comments

Comments
 (0)