Skip to content

Commit e9efb15

Browse files
committed
Document some various stuff better
Cleanup a couple of things
1 parent fac4c3f commit e9efb15

File tree

7 files changed

+51
-17
lines changed

7 files changed

+51
-17
lines changed

godot-core/src/builtin/meta/mod.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ pub struct PropertyInfo {
278278

279279
/// Which class this property is.
280280
///
281-
/// This should be set to [`ClassName::none()`] unless the variant type is object. You can use
281+
/// This should be set to [`ClassName::none()`] unless the variant type is `Object`. You can use
282282
/// [`GodotClass::class_name()`](crate::obj::GodotClass::class_name()) to get the right name to use here.
283283
pub class_name: ClassName,
284284

@@ -317,7 +317,7 @@ impl PropertyInfo {
317317

318318
/// Change the `hint` and `hint_string` to be the given `hint_info`.
319319
///
320-
/// See [`export_info_functions`](crate::property::export_info_function) for functions that return appropriate `PropertyHintInfo`s for
320+
/// See [`export_info_functions`](crate::property::export_info_functions) for functions that return appropriate `PropertyHintInfo`s for
321321
/// various Godot annotations.
322322
///
323323
/// # Examples
@@ -326,11 +326,11 @@ impl PropertyInfo {
326326
///
327327
// TODO: Make this nicer to use.
328328
/// ```no_run
329-
/// # use crate::property::export_info_function;
330-
/// # use crate::builtin::meta::PropertyInfo;
329+
/// use godot::register::property::export_info_functions;
330+
/// use godot::builtin::meta::PropertyInfo;
331331
///
332332
/// let property = PropertyInfo::new_export::<f64>("my_range_property")
333-
/// .with_hint_info(export_info_function::export_range(
333+
/// .with_hint_info(export_info_functions::export_range(
334334
/// 0.0,
335335
/// 10.0,
336336
/// Some(0.1),
@@ -340,7 +340,7 @@ impl PropertyInfo {
340340
/// false,
341341
/// false,
342342
/// false,
343-
/// ))
343+
/// ));
344344
/// ```
345345
pub fn with_hint_info(self, hint_info: PropertyHintInfo) -> Self {
346346
let PropertyHintInfo { hint, hint_string } = hint_info;
@@ -415,7 +415,7 @@ impl PropertyInfo {
415415
/// [`free_owned_property_sys`](Self::free_owned_property_sys).
416416
///
417417
/// This will leak memory unless used together with `free_owned_property_sys`.
418-
pub fn into_owned_property_sys(self) -> sys::GDExtensionPropertyInfo {
418+
pub(crate) fn into_owned_property_sys(self) -> sys::GDExtensionPropertyInfo {
419419
use crate::obj::EngineBitfield as _;
420420
use crate::obj::EngineEnum as _;
421421

@@ -433,9 +433,9 @@ impl PropertyInfo {
433433
///
434434
/// # Safety
435435
///
436-
/// * Must only be used on a struct returned from a call to [`into_owned_property_sys`], without modification.
436+
/// * Must only be used on a struct returned from a call to `into_owned_property_sys`, without modification.
437437
/// * Must not be called more than once on a `sys::GDExtensionPropertyInfo` struct.
438-
pub unsafe fn free_owned_property_sys(info: sys::GDExtensionPropertyInfo) {
438+
pub(crate) unsafe fn free_owned_property_sys(info: sys::GDExtensionPropertyInfo) {
439439
// SAFETY: This function was called on a pointer returned from `into_owned_property_sys`, thus both `info.name` and
440440
// `info.hint_string` were created from calls to `into_owned_string_sys` on their respective types.
441441
// Additionally this function isn't called more than once on a struct containing the same `name` or `hint_string` pointers.

godot-core/src/builtin/string/gstring.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ impl GString {
153153
sys::static_assert_eq_size_align!(StringName, sys::types::OpaqueString);
154154

155155
let ptr = ptr.cast::<Self>();
156+
157+
// SAFETY: `ptr` was returned from a call to `into_owned_string_sys`, which means it was created by a call to
158+
// `Box::into_raw`, thus we can use `Box::from_raw` here. Additionally this is only called once on this pointer.
156159
let boxed = unsafe { Box::from_raw(ptr) };
157160
*boxed
158161
}

godot-core/src/builtin/string/string_name.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ impl StringName {
134134
sys::static_assert_eq_size_align!(StringName, sys::types::OpaqueStringName);
135135

136136
let ptr = ptr.cast::<Self>();
137+
138+
// SAFETY: `ptr` was returned from a call to `into_owned_string_sys`, which means it was created by a call to
139+
// `Box::into_raw`, thus we can use `Box::from_raw` here. Additionally this is only called once on this pointer.
137140
let boxed = unsafe { Box::from_raw(ptr) };
138141
*boxed
139142
}

godot-core/src/property.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ impl PropertyHintInfo {
131131

132132
/// Functions used to translate user-provided arguments into export hints.
133133
///
134-
/// Each function is named the same as the equivalent Godot annotation. E.g `@export_range` is `fn export_range`.
134+
/// Each function is named the same as the equivalent Godot annotation. For instance `@export_range` in Godot is `fn export_range` here.
135135
pub mod export_info_functions {
136136
use crate::builtin::GString;
137137
use crate::engine::global::PropertyHint;
@@ -239,7 +239,8 @@ pub mod export_info_functions {
239239
/// # Examples
240240
///
241241
/// ```no_run
242-
/// export_enum(&[("a", None), ("b", "Some(10)")]);
242+
/// # use godot::register::property::export_info_functions::export_enum;
243+
/// export_enum(&[("a", None), ("b", Some(10))]);
243244
/// ```
244245
pub fn export_enum<T>(variants: &[T]) -> PropertyHintInfo
245246
where
@@ -271,7 +272,8 @@ pub mod export_info_functions {
271272
/// # Examples
272273
///
273274
/// ```no_run
274-
/// export_flags(&[("a", None), ("b", "Some(10)")]);
275+
/// # use godot::register::property::export_info_functions::export_flags;
276+
/// export_flags(&[("a", None), ("b", Some(10))]);
275277
/// ```
276278
pub fn export_flags<T>(bits: &[T]) -> PropertyHintInfo
277279
where

godot-core/src/registry/callbacks.rs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,9 @@ pub unsafe extern "C" fn unreference<T: GodotClass>(instance: sys::GDExtensionCl
201201
storage.on_dec_ref();
202202
}
203203

204+
/// # Safety
205+
///
206+
/// Must only be called by Godot as a callback for `get_property_list` for a rust-defined class of type `T`.
204207
#[deny(unsafe_op_in_unsafe_fn)]
205208
pub unsafe extern "C" fn get_property_list<T: cap::GodotGetPropertyList>(
206209
instance: sys::GDExtensionClassInstancePtr,
@@ -216,6 +219,7 @@ pub unsafe extern "C" fn get_property_list<T: cap::GodotGetPropertyList>(
216219
.map(|prop| prop.into_owned_property_sys())
217220
.collect();
218221

222+
// SAFETY: Godot ensures that `count` is initialized and valid to write into.
219223
unsafe {
220224
*count = property_list_sys
221225
.len()
@@ -226,20 +230,32 @@ pub unsafe extern "C" fn get_property_list<T: cap::GodotGetPropertyList>(
226230
Box::leak(property_list_sys).as_mut_ptr()
227231
}
228232

233+
/// # Safety
234+
///
235+
/// - Must only be called by Godot as a callback for `free_property_list` for a rust-defined class of type `T`.
236+
/// - Must only be passed to Godot as a callback when [`get_property_list`] is the corresponding `get_property_list` callback.
229237
#[deny(unsafe_op_in_unsafe_fn)]
230238
pub unsafe extern "C" fn free_property_list<T: cap::GodotGetPropertyList>(
231239
_instance: sys::GDExtensionClassInstancePtr,
232240
list: *const sys::GDExtensionPropertyInfo,
233241
count: u32,
234242
) {
235243
let list = list as *mut sys::GDExtensionPropertyInfo;
236-
let property_list_sys = unsafe {
237-
Box::from_raw(std::slice::from_raw_parts_mut(
238-
list,
239-
count.try_into().expect("`u32` should fit in `usize`"),
240-
))
244+
245+
// SAFETY: `list` comes from `get_property_list` above, and `count` also comes from the same function.
246+
// This means that `list` is a pointer to a `&[sys::GDExtensionPropertyInfo]` slice of length `count`.
247+
// This means all the preconditions of this function are satisfied except uniqueness of this point.
248+
// Uniqueness is guaranteed as Godot called this function at a point where the list is no longer accessed
249+
// through any other pointer, and we dont access the slice through any other pointer after this call either.
250+
let property_list_slice = unsafe {
251+
std::slice::from_raw_parts_mut(list, count.try_into().expect("`u32` should fit in `usize`"))
241252
};
242253

254+
// SAFETY: This slice was created by calling `Box::leak` on a `Box<[sys::GDExtensionPropertyInfo]>`, we can thus
255+
// call `Box::from_raw` on this slice to get back the original boxed slice.
256+
// Note that this relies on coercion of `&mut` -> `*mut`.
257+
let property_list_sys = unsafe { Box::from_raw(property_list_slice) };
258+
243259
for property_info in property_list_sys.iter() {
244260
// SAFETY: The structs contained in this list were all returned from `into_owned_property_sys`.
245261
// We only call this method once for each struct and for each list.
@@ -267,6 +283,9 @@ unsafe fn raw_property_get_revert<T: cap::GodotPropertyGetRevert>(
267283
T::__godot_property_get_revert(&*instance, property.clone())
268284
}
269285

286+
/// # Safety
287+
///
288+
/// - Must only be called by Godot as a callback for `property_can_revert` for a rust-defined class of type `T`.
270289
#[deny(unsafe_op_in_unsafe_fn)]
271290
pub unsafe extern "C" fn property_can_revert<T: cap::GodotPropertyGetRevert>(
272291
instance: sys::GDExtensionClassInstancePtr,
@@ -278,6 +297,9 @@ pub unsafe extern "C" fn property_can_revert<T: cap::GodotPropertyGetRevert>(
278297
revert.is_some() as sys::GDExtensionBool
279298
}
280299

300+
/// # Safety
301+
///
302+
/// - Must only be called by Godot as a callback for `property_get_revert` for a rust-defined class of type `T`.
281303
#[deny(unsafe_op_in_unsafe_fn)]
282304
pub unsafe extern "C" fn property_get_revert<T: cap::GodotPropertyGetRevert>(
283305
instance: sys::GDExtensionClassInstancePtr,

godot-core/src/registry/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ pub enum PluginItem {
190190
) -> *const sys::GDExtensionPropertyInfo,
191191
>,
192192

193+
// We do not support using this in Godot < 4.3, however it's easier to leave this in and fail elsewhere when attempting to use
194+
// this in Godot < 4.3.
193195
#[cfg(before_api = "4.3")]
194196
user_free_property_list_fn: Option<
195197
unsafe extern "C" fn(

itest/rust/src/object_tests/virtual_methods_test.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,8 @@ impl IRefCounted for RevertTest {
296296
match String::from(property).as_str() {
297297
"property_not_revert" => None,
298298
"property_do_revert" => Some(GString::from("hello!").to_variant()),
299+
// No UB or anything else like a crash or panic should happen when `property_can_revert` and `property_get_revert` return
300+
// inconsistent values, but in case something like that happens we should be able to detect it through this function.
299301
"property_changes" => {
300302
if INC.fetch_add(1, std::sync::atomic::Ordering::AcqRel) % 2 == 0 {
301303
None

0 commit comments

Comments
 (0)