Skip to content

Commit 3e80962

Browse files
committed
Add ptrlist container
Add into/free owned_sys methods for MethodList Remove helper functions that were just used in one place and which didnt really do much to improve readability
1 parent e4b5316 commit 3e80962

File tree

3 files changed

+235
-178
lines changed

3 files changed

+235
-178
lines changed

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,34 @@ impl Variant {
374374
// SAFETY: `variant_array` isn't null so it is safe to call `from_raw_parts_mut` on the pointer cast to `*mut Variant`.
375375
unsafe { std::slice::from_raw_parts_mut(variant_array, length) }
376376
}
377+
378+
/// Consumes self and turns it into a sys-ptr, should be used together with [`from_owned_var_sys`](Self::from_owned_var_sys).
379+
///
380+
/// This will leak memory unless `from_owned_var_sys` is called on the returned pointer.
381+
pub(crate) fn into_owned_var_sys(self) -> sys::GDExtensionVariantPtr {
382+
sys::static_assert_eq_size_align!(Variant, sys::types::OpaqueVariant);
383+
384+
let leaked = Box::into_raw(Box::new(self));
385+
leaked.cast()
386+
}
387+
388+
/// Creates a `Variant` from a sys-ptr without incrementing the refcount.
389+
///
390+
/// # Safety
391+
///
392+
/// * Must only be used on a pointer returned from a call to [`into_owned_var_sys`](Self::into_owned_var_sys).
393+
/// * Must not be called more than once on the same pointer.
394+
#[deny(unsafe_op_in_unsafe_fn)]
395+
pub(crate) unsafe fn from_owned_var_sys(ptr: sys::GDExtensionVariantPtr) -> Self {
396+
sys::static_assert_eq_size_align!(Variant, sys::types::OpaqueVariant);
397+
398+
let ptr = ptr.cast::<Self>();
399+
400+
// SAFETY: `ptr` was returned from a call to `into_owned_var_sys`, which means it was created by a call to
401+
// `Box::into_raw`, thus we can use `Box::from_raw` here. Additionally this is only called once on this pointer.
402+
let boxed = unsafe { Box::from_raw(ptr) };
403+
*boxed
404+
}
377405
}
378406

379407
impl ArrayElement for Variant {}

godot-core/src/meta/mod.rs

Lines changed: 99 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -241,50 +241,114 @@ pub struct MethodInfo {
241241
}
242242

243243
impl MethodInfo {
244-
/// Converts to the FFI type. Keep this object allocated while using that!
245-
///
246-
/// The struct returned by this function contains pointers into the fields of `self`. `self` should therefore not be dropped while the
247-
/// `sys::GDExtensionMethodInfo` is still in use.
248-
///
249-
/// This function also leaks memory that has to be cleaned up by the caller once it is no longer used. Specifically the `arguments` and
250-
/// `default_arguments` vectors have to be reconstructed from the pointer and length and then dropped/freed.
244+
/// Consumes self and turns it into a `sys::GDExtensionMethodInfo`, should be used together with
245+
/// [`free_owned_method_sys`](Self::free_owned_method_sys).
251246
///
252-
/// Each vector can be reconstructed with `Vec::from_raw_parts` since the pointers were created with `Vec::into_boxed_slice`, which
253-
/// guarantees that the vector capacity and length are equal.
254-
pub fn method_sys(&self) -> sys::GDExtensionMethodInfo {
247+
/// This will leak memory unless used together with `free_owned_method_sys`.
248+
pub fn into_owned_method_sys(self) -> sys::GDExtensionMethodInfo {
255249
use crate::obj::EngineBitfield as _;
256250

257-
let argument_count = self.arguments.len() as u32;
258-
let argument_vec = self
259-
.arguments
260-
.iter()
261-
.map(|arg| arg.property_sys())
262-
.collect::<Vec<_>>()
263-
.into_boxed_slice();
264-
265-
// SAFETY: dereferencing the new box pointer is fine as it is guaranteed to not be null
266-
let arguments = unsafe { (*Box::into_raw(argument_vec)).as_mut_ptr() };
267-
268-
let default_argument_count = self.default_arguments.len() as u32;
269-
let default_argument_vec = self
270-
.default_arguments
271-
.iter()
272-
.map(|arg| sys::SysPtr::force_mut(arg.var_sys()))
273-
.collect::<Vec<_>>()
274-
.into_boxed_slice();
275-
276-
// SAFETY: dereferencing the new box pointer is fine as it is guaranteed to not be null
277-
let default_arguments = unsafe { (*Box::into_raw(default_argument_vec)).as_mut_ptr() };
251+
// Destructure self to ensure all fields are used.
252+
let Self {
253+
id,
254+
method_name,
255+
// TODO: Do we need this?
256+
class_name: _class_name,
257+
return_type,
258+
arguments,
259+
default_arguments,
260+
flags,
261+
} = self;
262+
263+
let argument_count: u32 = arguments
264+
.len()
265+
.try_into()
266+
.expect("cannot have more than `u32::MAX` arguments");
267+
let arguments = arguments
268+
.into_iter()
269+
.map(|arg| arg.into_owned_property_sys())
270+
.collect::<Box<[_]>>();
271+
let arguments = Box::leak(arguments).as_mut_ptr();
272+
273+
let default_argument_count: u32 = default_arguments
274+
.len()
275+
.try_into()
276+
.expect("cannot have more than `u32::MAX` default arguments");
277+
let default_argument = default_arguments
278+
.into_iter()
279+
.map(|arg| arg.into_owned_var_sys())
280+
.collect::<Box<[_]>>();
281+
let default_arguments = Box::leak(default_argument).as_mut_ptr();
278282

279283
sys::GDExtensionMethodInfo {
280-
id: self.id,
281-
name: sys::SysPtr::force_mut(self.method_name.string_sys()),
282-
return_value: self.return_type.property_sys(),
284+
id,
285+
name: method_name.into_owned_string_sys(),
286+
return_value: return_type.into_owned_property_sys(),
283287
argument_count,
284288
arguments,
285289
default_argument_count,
286290
default_arguments,
287-
flags: u32::try_from(self.flags.ord()).expect("flags should be valid"),
291+
flags: flags.ord().try_into().expect("flags should be valid"),
288292
}
289293
}
294+
295+
/// Properly frees a `sys::GDExtensionMethodInfo` created by [`into_owned_method_sys`](Self::into_owned_method_sys).
296+
///
297+
/// # Safety
298+
///
299+
/// * Must only be used on a struct returned from a call to `into_owned_method_sys`, without modification.
300+
/// * Must not be called more than once on a `sys::GDExtensionMethodInfo` struct.
301+
#[deny(unsafe_op_in_unsafe_fn)]
302+
pub unsafe fn free_owned_method_sys(info: sys::GDExtensionMethodInfo) {
303+
// Destructure info to ensure all fields are used.
304+
let sys::GDExtensionMethodInfo {
305+
name,
306+
return_value,
307+
flags: _flags,
308+
id: _id,
309+
argument_count,
310+
arguments,
311+
default_argument_count,
312+
default_arguments,
313+
} = info;
314+
315+
// SAFETY: `name` and `return_value` were created from the appropriate method calls, and have not been freed before this.
316+
unsafe {
317+
let _name = StringName::from_owned_string_sys(name);
318+
PropertyInfo::free_owned_property_sys(return_value);
319+
}
320+
321+
// SAFETY: These pointers were both created from a call to `as_mut_ptr` on a slice. Additionally these pointer will not be accessed
322+
// again after this function call.
323+
let (arguments_slice, default_arguments_slice) = unsafe {
324+
(
325+
std::slice::from_raw_parts_mut(
326+
arguments,
327+
argument_count
328+
.try_into()
329+
.expect("gdext only supports targets where u32 <= usize"),
330+
),
331+
std::slice::from_raw_parts_mut(
332+
default_arguments,
333+
default_argument_count
334+
.try_into()
335+
.expect("gdext only supports targets where u32 <= usize"),
336+
),
337+
)
338+
};
339+
340+
// SAFETY: We have exclusive ownership of these slices, and they were originally created from a call to `Box::leak`.
341+
let (_arguments, default_arguments) = unsafe {
342+
(
343+
Box::from_raw(arguments_slice),
344+
Box::from_raw(default_arguments_slice),
345+
)
346+
};
347+
348+
default_arguments.iter().for_each(|ptr| {
349+
// SAFETY: These pointers were originally created from a call to `Variant::into_owner_var_sys`, and this method will not be
350+
// called again on this pointer.
351+
let _variant = unsafe { Variant::from_owned_var_sys(*ptr) };
352+
});
353+
}
290354
}

0 commit comments

Comments
 (0)