diff mbox series

[23/26] rust: qom: add initial subset of methods on Object

Message ID 20241209123717.99077-24-pbonzini@redhat.com (mailing list archive)
State New
Headers show
Series rust: bundle of prerequisites for HPET implementation | expand

Commit Message

Paolo Bonzini Dec. 9, 2024, 12:37 p.m. UTC
Add an example of implementing instance methods and converting the
result back to a Rust type.  In this case the returned types are a
string (actually a Cow<str>; but that's transparent as long as it derefs
to &str) and a QOM class.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 rust/qemu-api/src/prelude.rs |  1 +
 rust/qemu-api/src/qom.rs     | 56 ++++++++++++++++++++++++++++++++++--
 rust/qemu-api/tests/tests.rs | 12 ++++++++
 3 files changed, 66 insertions(+), 3 deletions(-)

Comments

Zhao Liu Dec. 16, 2024, 3:18 p.m. UTC | #1
On Mon, Dec 09, 2024 at 01:37:14PM +0100, Paolo Bonzini wrote:
> Date: Mon,  9 Dec 2024 13:37:14 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 23/26] rust: qom: add initial subset of methods on Object
> X-Mailer: git-send-email 2.47.1
> 
> Add an example of implementing instance methods and converting the
> result back to a Rust type.  In this case the returned types are a
> string (actually a Cow<str>; but that's transparent as long as it derefs
> to &str) and a QOM class.
> 
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  rust/qemu-api/src/prelude.rs |  1 +
>  rust/qemu-api/src/qom.rs     | 56 ++++++++++++++++++++++++++++++++++--
>  rust/qemu-api/tests/tests.rs | 12 ++++++++
>  3 files changed, 66 insertions(+), 3 deletions(-)
> 

Good example!

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
diff mbox series

Patch

diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
index a0a71fcd6bc..6f32deeb2ed 100644
--- a/rust/qemu-api/src/prelude.rs
+++ b/rust/qemu-api/src/prelude.rs
@@ -12,6 +12,7 @@ 
 pub use crate::qom::ObjectCast;
 pub use crate::qom::ObjectCastMut;
 pub use crate::qom::ObjectDeref;
+pub use crate::qom::ObjectMethods;
 pub use crate::qom::ObjectType;
 
 pub use crate::qom_isa;
diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs
index 7115d85df6f..d7475b8ed6a 100644
--- a/rust/qemu-api/src/qom.rs
+++ b/rust/qemu-api/src/qom.rs
@@ -5,8 +5,8 @@ 
 //! Bindings to access QOM functionality from Rust.
 //!
 //! The QEMU Object Model (QOM) provides inheritance and dynamic typing for QEMU
-//! devices. This module makes QOM's features available in Rust through two main
-//! mechanisms:
+//! devices. This module makes QOM's features available in Rust through three
+//! main mechanisms:
 //!
 //! * Automatic creation and registration of `TypeInfo` for classes that are
 //!   written in Rust, as well as mapping between Rust traits and QOM vtables.
@@ -15,6 +15,11 @@ 
 //!   trait and methods such as [`upcast`](ObjectCast::upcast) and
 //!   [`downcast`](ObjectCast::downcast).
 //!
+//! * Automatic delegation of parent class methods to child classes. When a
+//!   trait uses [`IsA`] as a bound, its contents become available to all child
+//!   classes through blanket implementations. This works both for class methods
+//!   and for instance methods accessed through references or smart pointers.
+//!
 //! # Structure of a class
 //!
 //! A concrete class only needs a struct holding instance state. The struct must
@@ -40,6 +45,16 @@ 
 //!   `ClassInitImpl<DeviceClass>`. This fills the vtable in the class struct,
 //!   typically with wrappers that call into the
 //!   [`DeviceImpl`](crate::qdev::DeviceImpl) implementations.
+//!
+//! * a trait for instance methods, for example `DeviceMethods`. This trait is
+//!   automatically implemented for any reference or smart pointer to a device
+//!   instance.  It calls into the vtable provides access across all subclasses
+//!   to methods defined for the class.
+//!
+//! * optionally, a trait for class methods, for example `DeviceClassMethods`.
+//!   This provides access to class-wide functionality that doesn't depend on
+//!   instance data. Like instance methods, these are automatically inherited by
+//!   child classes.
 
 use std::{
     ffi::CStr,
@@ -49,7 +64,7 @@ 
 
 pub use bindings::{Object, ObjectClass};
 
-use crate::bindings::{self, object_dynamic_cast, TypeInfo};
+use crate::bindings::{self, object_dynamic_cast, object_get_class, object_get_typename, TypeInfo};
 
 /// Marker trait: `Self` can be statically upcasted to `P` (i.e. `P` is a direct
 /// or indirect parent of `Self`).
@@ -504,3 +519,38 @@  unsafe impl ObjectType for Object {
     const TYPE_NAME: &'static CStr =
         unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_OBJECT) };
 }
+
+/// Trait for methods exposed by the Object class.  The methods can be
+/// called on all objects that have the trait `IsA<Object>`.
+///
+/// The trait should only be used through the blanket implementation,
+/// which guarantees safety via `IsA`
+pub trait ObjectMethods: ObjectDeref
+where
+    Self::Target: IsA<Object>,
+{
+    /// Return the name of the type of `self`
+    fn typename(&self) -> std::borrow::Cow<'_, str> {
+        let obj = self.upcast::<Object>();
+        // SAFETY: safety of this is the requirement for implementing IsA
+        // The result of the C API has static lifetime
+        unsafe {
+            let p = object_get_typename(obj.as_mut_ptr());
+            CStr::from_ptr(p).to_string_lossy()
+        }
+    }
+
+    fn get_class(&self) -> &'static <Self::Target as ObjectType>::Class {
+        let obj = self.upcast::<Object>();
+
+        // SAFETY: all objects can call object_get_class; the actual class
+        // type is guaranteed by the implementation of `ObjectType` and
+        // `ObjectImpl`.
+        let klass: &'static <Self::Target as ObjectType>::Class =
+            unsafe { &*object_get_class(obj.as_mut_ptr()).cast() };
+
+        klass
+    }
+}
+
+impl<R: ObjectDeref> ObjectMethods for R where R::Target: IsA<Object> {}
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index 549e9699c26..4c766c7f808 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -88,6 +88,18 @@  fn test_object_new() {
     }
 }
 
+#[test]
+/// Try invoking a method on an object.
+fn test_typename() {
+    init_qom();
+    let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() };
+    let p_ref: &DummyState = unsafe { &*p };
+    assert_eq!(p_ref.typename(), "dummy");
+    unsafe {
+        object_unref(p_ref.as_object_mut_ptr().cast::<c_void>());
+    }
+}
+
 // a note on all "cast" tests: usually, especially for downcasts the desired
 // class would be placed on the right, for example:
 //