rust: qom: add possibility of overriding unparent

Add a blanket definition of ClassInitImpl<ObjectClass> that thunks
ObjectImpl::UNPARENT and overrides it in ObjectClass if it is not
None.

ClassInitImpl<DeviceClass> can now call its superclass's ClassInitImpl,
so that the C and Rust hierarchies match more closely.

This is mostly done as an example of implementing the metaclass
hierarchy under ClassInitImpl.

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Paolo Bonzini 2024-10-29 15:00:26 +01:00
parent 6dd818fbbb
commit cb36da9bd8
2 changed files with 45 additions and 5 deletions

View file

@ -6,7 +6,7 @@
use std::{ffi::CStr, os::raw::c_void}; use std::{ffi::CStr, os::raw::c_void};
use crate::bindings::{Object, ObjectClass, TypeInfo}; use crate::bindings::{self, Object, ObjectClass, TypeInfo};
unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut Object) { unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut Object) {
// SAFETY: obj is an instance of T, since rust_instance_init<T> // SAFETY: obj is an instance of T, since rust_instance_init<T>
@ -121,6 +121,9 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
class_data: core::ptr::null_mut(), class_data: core::ptr::null_mut(),
interfaces: core::ptr::null_mut(), interfaces: core::ptr::null_mut(),
}; };
// methods on ObjectClass
const UNPARENT: Option<fn(&Self)> = None;
} }
/// Internal trait used to automatically fill in a class struct. /// Internal trait used to automatically fill in a class struct.
@ -134,7 +137,8 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
/// ///
/// Each struct will implement this trait with `T` equal to each /// Each struct will implement this trait with `T` equal to each
/// superclass. For example, a device should implement at least /// superclass. For example, a device should implement at least
/// `ClassInitImpl<`[`DeviceClass`](crate::bindings::DeviceClass)`>`. /// `ClassInitImpl<`[`DeviceClass`](crate::bindings::DeviceClass)`>` and
/// `ClassInitImpl<`[`ObjectClass`](crate::bindings::ObjectClass)`>`.
/// Such implementations are made in one of two ways. /// Such implementations are made in one of two ways.
/// ///
/// For most superclasses, `ClassInitImpl` is provided by the `qemu-api` /// For most superclasses, `ClassInitImpl` is provided by the `qemu-api`
@ -147,9 +151,13 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
/// ```ignore /// ```ignore
/// impl<T> ClassInitImpl<DeviceClass> for T /// impl<T> ClassInitImpl<DeviceClass> for T
/// where /// where
/// T: DeviceImpl, /// T: ClassInitImpl<ObjectClass> + DeviceImpl,
/// ``` /// ```
/// ///
/// The bound on `ClassInitImpl<ObjectClass>` is needed so that,
/// after initializing the `DeviceClass` part of the class struct,
/// the parent [`ObjectClass`] is initialized as well.
///
/// The other case is when manual implementation of the trait is needed. /// The other case is when manual implementation of the trait is needed.
/// This covers the following cases: /// This covers the following cases:
/// ///
@ -235,3 +243,33 @@ macro_rules! module_init {
} }
}; };
} }
/// # Safety
///
/// We expect the FFI user of this function to pass a valid pointer that
/// can be downcasted to type `T`. We also expect the device is
/// readable/writeable from one thread at any time.
unsafe extern "C" fn rust_unparent_fn<T: ObjectImpl>(dev: *mut Object) {
unsafe {
assert!(!dev.is_null());
let state = core::ptr::NonNull::new_unchecked(dev.cast::<T>());
T::UNPARENT.unwrap()(state.as_ref());
}
}
impl<T> ClassInitImpl<ObjectClass> for T
where
T: ObjectImpl,
{
fn class_init(oc: &mut ObjectClass) {
if <T as ObjectImpl>::UNPARENT.is_some() {
oc.unparent = Some(rust_unparent_fn::<T>);
}
}
}
unsafe impl ObjectType for Object {
type Class = ObjectClass;
const TYPE_NAME: &'static CStr =
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_OBJECT) };
}

View file

@ -5,7 +5,7 @@
use std::ffi::CStr; use std::ffi::CStr;
use crate::{ use crate::{
bindings::{self, DeviceClass, DeviceState, Error, Property, VMStateDescription}, bindings::{self, DeviceClass, DeviceState, Error, ObjectClass, Property, VMStateDescription},
definitions::ClassInitImpl, definitions::ClassInitImpl,
prelude::*, prelude::*,
}; };
@ -68,7 +68,7 @@ unsafe extern "C" fn rust_reset_fn<T: DeviceImpl>(dev: *mut DeviceState) {
impl<T> ClassInitImpl<DeviceClass> for T impl<T> ClassInitImpl<DeviceClass> for T
where where
T: DeviceImpl, T: ClassInitImpl<ObjectClass> + DeviceImpl,
{ {
fn class_init(dc: &mut DeviceClass) { fn class_init(dc: &mut DeviceClass) {
if <T as DeviceImpl>::REALIZE.is_some() { if <T as DeviceImpl>::REALIZE.is_some() {
@ -88,6 +88,8 @@ where
bindings::device_class_set_props_n(dc, prop.as_ptr(), prop.len()); bindings::device_class_set_props_n(dc, prop.as_ptr(), prop.len());
} }
} }
<T as ClassInitImpl<ObjectClass>>::class_init(&mut dc.parent_class);
} }
} }