[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 09/14] rust: add idiomatic bindings to define Object subclasses
From: |
Paolo Bonzini |
Subject: |
[PATCH 09/14] rust: add idiomatic bindings to define Object subclasses |
Date: |
Mon, 1 Jul 2024 16:58:41 +0200 |
Provide a macro to register a type and automatically define instance_init
(actually instance_mem_init) and instance_finalize functions. Subclasses
of Object must define a trait ObjectImpl, to point the type definition
machinery to the implementation of virtual functions in Object.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
qemu/src/lib.rs | 4 +
qemu/src/qom/mod.rs | 1 +
qemu/src/qom/object_impl.rs | 146 ++++++++++++++++++++++++++++++++++++
qemu/src/util/mod.rs | 1 +
qemu/src/util/zeroed.rs | 21 ++++++
qemu/tests/main.rs | 32 ++++++++
6 files changed, 205 insertions(+)
create mode 100644 qemu/src/qom/object_impl.rs
create mode 100644 qemu/src/util/zeroed.rs
create mode 100644 qemu/tests/main.rs
diff --git a/qemu/src/lib.rs b/qemu/src/lib.rs
index b0dcce1..81abf9c 100644
--- a/qemu/src/lib.rs
+++ b/qemu/src/lib.rs
@@ -4,6 +4,7 @@
pub mod bindings;
pub use bindings::DeviceState;
pub use bindings::Object;
+pub use bindings::TypeInfo;
pub mod hw;
pub use hw::core::device::DeviceMethods;
@@ -12,6 +13,8 @@ pub mod qom;
pub use qom::object::ObjectClassMethods;
pub use qom::object::ObjectMethods;
pub use qom::object::ObjectType;
+pub use qom::object_impl::ObjectImpl;
+pub use qom::object_impl::TypeImpl;
pub use qom::refs::ObjectCast;
pub use qom::refs::Owned;
@@ -21,4 +24,5 @@ pub use util::foreign::CloneToForeign;
pub use util::foreign::FromForeign;
pub use util::foreign::IntoNative;
pub use util::foreign::OwnedPointer;
+pub use util::zeroed::Zeroed;
pub type Result<T> = std::result::Result<T, Error>;
diff --git a/qemu/src/qom/mod.rs b/qemu/src/qom/mod.rs
index 95489c5..3f8ee6e 100644
--- a/qemu/src/qom/mod.rs
+++ b/qemu/src/qom/mod.rs
@@ -1,2 +1,3 @@
pub mod object;
+pub mod object_impl;
pub mod refs;
diff --git a/qemu/src/qom/object_impl.rs b/qemu/src/qom/object_impl.rs
new file mode 100644
index 0000000..61546b6
--- /dev/null
+++ b/qemu/src/qom/object_impl.rs
@@ -0,0 +1,146 @@
+//! Macros and traits to implement subclasses of Object in Rust
+//!
+//! @author Paolo Bonzini
+
+#![allow(clippy::missing_safety_doc)]
+
+use const_default::ConstDefault;
+
+use std::ffi::c_void;
+use std::mem;
+use std::mem::MaybeUninit;
+use std::ptr::drop_in_place;
+
+use crate::qom::object::ObjectType;
+
+use crate::qom::refs::ObjectCast;
+
+use crate::bindings::type_register;
+use crate::bindings::Object;
+use crate::bindings::ObjectClass;
+use crate::bindings::TypeInfo;
+
+use crate::util::zeroed::Zeroed;
+
+/// Information on which superclass methods are overridden
+/// by a Rust-implemented subclass of Object.
+pub trait ObjectImpl: ObjectType {
+ /// If not `None`, a function that implements the `unparent` member
+ /// of the QOM `ObjectClass`.
+ const UNPARENT: Option<fn(obj: &Self)> = None;
+}
+
+impl ObjectClass {
+ /// Initialize an `ObjectClass` from an `ObjectImpl`.
+ pub fn class_init<T: ObjectImpl>(&mut self) {
+ unsafe extern "C" fn rust_unparent<T: ObjectImpl>(obj: *mut Object) {
+ let f = T::UNPARENT.unwrap();
+ f((&*obj).unsafe_cast::<T>())
+ }
+ self.unparent = T::UNPARENT.map(|_| rust_unparent::<T> as _);
+ }
+}
+
+impl Object {
+ pub unsafe extern "C" fn rust_class_init<T: ObjectImpl>(
+ klass: *mut c_void,
+ _data: *mut c_void,
+ ) {
+ let oc: &mut ObjectClass = &mut *(klass.cast());
+ oc.class_init::<T>();
+ }
+}
+
+/// Internal information on a Rust-implemented subclass of Object.
+/// Only public because it is used by macros.
+pub unsafe trait TypeImpl: ObjectType + ObjectImpl {
+ type Super: ObjectType;
+ type Conf: ConstDefault;
+ type State: Default;
+
+ const CLASS_INIT: unsafe extern "C" fn(klass: *mut c_void, data: *mut
c_void);
+
+ fn uninit_conf(obj: &mut MaybeUninit<Self>) -> &mut
MaybeUninit<Self::Conf>;
+ fn uninit_state(obj: &mut MaybeUninit<Self>) -> &mut
MaybeUninit<Self::State>;
+}
+
+unsafe fn rust_type_register<T: TypeImpl + ObjectImpl>() {
+ unsafe extern "C" fn rust_instance_mem_init<T: TypeImpl>(obj: *mut c_void)
{
+ let obj: &mut std::mem::MaybeUninit<T> = &mut *(obj.cast());
+
+ T::uninit_conf(obj).write(ConstDefault::DEFAULT);
+ T::uninit_state(obj).write(Default::default());
+ }
+
+ unsafe extern "C" fn rust_instance_finalize<T: TypeImpl>(obj: *mut c_void)
{
+ let obj: *mut T = obj.cast();
+ drop_in_place(obj);
+ }
+
+ let ti = TypeInfo {
+ name: T::TYPE.as_ptr(),
+ parent: T::Super::TYPE.as_ptr(),
+ instance_size: mem::size_of::<T>(),
+ instance_mem_init: Some(rust_instance_mem_init::<T>),
+ instance_finalize: Some(rust_instance_finalize::<T>),
+ class_init: Some(T::CLASS_INIT),
+
+ // SAFETY: TypeInfo is defined in C and all fields are okay to be
zeroed
+ ..Zeroed::zeroed()
+ };
+
+ type_register(&ti)
+}
+
+#[macro_export]
+macro_rules! qom_define_type {
+ ($name:expr, $struct:ident, $conf_ty:ty, $state_ty:ty; @extends $super:ty
$(,$supers:ty)*) => {
+ struct $struct {
+ // self.base dropped by call to superclass instance_finalize
+ base: std::mem::ManuallyDrop<$super>,
+ conf: $conf_ty,
+ state: $state_ty,
+ }
+
+ // Define IsA markers for the struct itself and all the superclasses
+ $crate::qom_isa!($struct, $super $(,$supers)*);
+
+ unsafe impl $crate::qom::object::ObjectType for $struct {
+ const TYPE: &'static std::ffi::CStr = $name;
+ }
+
+ unsafe impl $crate::qom::object_impl::TypeImpl for $struct {
+ type Super = $super;
+ type Conf = $conf_ty;
+ type State = $state_ty;
+
+ const CLASS_INIT: unsafe extern "C" fn(klass: *mut
std::ffi::c_void, data: *mut std::ffi::c_void)
+ = <$super>::rust_class_init::<Self>;
+
+ fn uninit_conf(obj: &mut std::mem::MaybeUninit::<Self>) -> &mut
std::mem::MaybeUninit<$conf_ty> {
+ use std::ptr::addr_of_mut;
+
+ // Projecting the incoming reference to a single field is safe,
+ // because the return value is also MaybeUnit
+ unsafe { &mut *(addr_of_mut!((*obj.as_mut_ptr()).conf).cast())
}
+ }
+
+ fn uninit_state(obj: &mut std::mem::MaybeUninit::<Self>) -> &mut
std::mem::MaybeUninit<$state_ty> {
+ use std::ptr::addr_of_mut;
+
+ // Projecting the incoming reference to a single field is safe,
+ // because the return value is also MaybeUnit
+ unsafe { &mut
*(addr_of_mut!((*obj.as_mut_ptr()).state).cast()) }
+ }
+ }
+
+ // TODO: call rust_type_register
+ };
+}
+
+#[macro_export]
+macro_rules! conf_type {
+ ($type:ty) => {
+ <$type as $crate::qom::object_impl::TypeImpl>::Conf
+ };
+}
diff --git a/qemu/src/util/mod.rs b/qemu/src/util/mod.rs
index e6078ac..9c081b6 100644
--- a/qemu/src/util/mod.rs
+++ b/qemu/src/util/mod.rs
@@ -1,2 +1,3 @@
pub mod error;
pub mod foreign;
+pub mod zeroed;
diff --git a/qemu/src/util/zeroed.rs b/qemu/src/util/zeroed.rs
new file mode 100644
index 0000000..e656834
--- /dev/null
+++ b/qemu/src/util/zeroed.rs
@@ -0,0 +1,21 @@
+#![allow(clippy::undocumented_unsafe_blocks)]
+
+use std::mem::MaybeUninit;
+
+/// Trait providing an easy way to obtain an all-zero
+/// value for a struct
+///
+/// # Safety
+///
+/// Only add this to a type if `MaybeUninit::zeroed().assume_init()`
+/// is valid for that type.
+pub unsafe trait Zeroed: Sized {
+ fn zeroed() -> Self {
+ // SAFETY: If this weren't safe, just do not add the
+ // trait to a type.
+ unsafe { MaybeUninit::zeroed().assume_init() }
+ }
+}
+
+// Put here all the impls that you need for the bindgen-provided types.
+unsafe impl Zeroed for crate::bindings::TypeInfo {}
diff --git a/qemu/tests/main.rs b/qemu/tests/main.rs
new file mode 100644
index 0000000..a7cbeed
--- /dev/null
+++ b/qemu/tests/main.rs
@@ -0,0 +1,32 @@
+use const_default::ConstDefault;
+
+use qemu::qom_define_type;
+use qemu::Object;
+use qemu::ObjectClassMethods;
+use qemu::ObjectImpl;
+
+#[derive(Default, ConstDefault)]
+struct TestConf {
+ #[allow(dead_code)]
+ foo: bool,
+}
+
+#[derive(Default)]
+struct TestState {
+ #[allow(dead_code)]
+ bar: i32,
+}
+
+qom_define_type!(
+ c"test-object",
+ TestObject,
+ TestConf,
+ ();
+ @extends Object
+);
+
+impl ObjectImpl for TestObject {}
+
+fn main() {
+ drop(TestObject::new());
+}
--
2.45.2
- [PATCH 02/14] set expectations, (continued)
- [PATCH 02/14] set expectations, Paolo Bonzini, 2024/07/01
- [PATCH 04/14] rust: add tests for util::foreign, Paolo Bonzini, 2024/07/01
- [PATCH 05/14] rust: define wrappers for Error, Paolo Bonzini, 2024/07/01
- [PATCH 03/14] rust: define traits and pointer wrappers to convert from/to C representations, Paolo Bonzini, 2024/07/01
- [PATCH 07/14] rust: define wrappers for methods of the QOM Object class, Paolo Bonzini, 2024/07/01
- [PATCH 08/14] rust: define wrappers for methods of the QOM Device class, Paolo Bonzini, 2024/07/01
- [PATCH 06/14] rust: define wrappers for basic QOM concepts, Paolo Bonzini, 2024/07/01
- [PATCH 09/14] rust: add idiomatic bindings to define Object subclasses,
Paolo Bonzini <=
- [PATCH 10/14] rust: add idiomatic bindings to define Device subclasses, Paolo Bonzini, 2024/07/01
- [PATCH 12/14] rust: replace c"" literals with cstr crate, Paolo Bonzini, 2024/07/01
- [PATCH 14/14] rust: use version of toml_edit that does not require new Rust, Paolo Bonzini, 2024/07/01
- [PATCH 13/14] rust: introduce alternative to offset_of!, Paolo Bonzini, 2024/07/01
- [PATCH 11/14] rust: replace std::ffi::c_char with libc::c_char, Paolo Bonzini, 2024/07/01
- Re: [PATCH 00/14] rust: example of bindings code for Rust in QEMU, Pierrick Bouvier, 2024/07/04