diff options
author | Paolo Bonzini <pbonzini@redhat.com> | 2024-11-06 00:01:57 +0100 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2024-12-19 19:36:37 +0100 |
commit | ba3b81f3b668d3faa6cbdb39e123394f7bf637c7 (patch) | |
tree | 42d37373c9ad3d1004e2565e7b2e98647f3916d3 | |
parent | f50cd85c8475c16374d0e138efda222ce4455f53 (diff) | |
download | qemu-ba3b81f3b668d3faa6cbdb39e123394f7bf637c7.zip qemu-ba3b81f3b668d3faa6cbdb39e123394f7bf637c7.tar.gz qemu-ba3b81f3b668d3faa6cbdb39e123394f7bf637c7.tar.bz2 |
rust: qom: add initial subset of methods on Object
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.
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-rw-r--r-- | rust/qemu-api/src/prelude.rs | 1 | ||||
-rw-r--r-- | rust/qemu-api/src/qom.rs | 56 | ||||
-rw-r--r-- | rust/qemu-api/tests/tests.rs | 12 |
3 files changed, 66 insertions, 3 deletions
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index a0a71fc..6f32dee 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -12,6 +12,7 @@ pub use crate::qom::Object; 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 74ea572..7d5fbef 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 leaf class only needs a struct holding instance state. The struct must @@ -37,6 +42,16 @@ //! `ClassInitImpl<DeviceClass>`. This fills the vtable in the class struct; //! the source for this is the `*Impl` trait; the associated consts and //! functions if needed are wrapped to map C types into Rust types. +//! +//! * 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, @@ -46,7 +61,7 @@ use std::{ 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`). @@ -532,3 +547,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 7b63e28..1d2825b 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: // |