 ba3b81f3b6
			
		
	
	
		ba3b81f3b6
		
	
	
	
	
		
			
			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>
		
			
				
	
	
		
			162 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			162 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| // Copyright 2024, Linaro Limited
 | |
| // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
 | |
| // SPDX-License-Identifier: GPL-2.0-or-later
 | |
| 
 | |
| use std::{
 | |
|     ffi::CStr,
 | |
|     os::raw::c_void,
 | |
|     ptr::{addr_of, addr_of_mut},
 | |
| };
 | |
| 
 | |
| use qemu_api::{
 | |
|     bindings::*,
 | |
|     c_str,
 | |
|     cell::{self, BqlCell},
 | |
|     declare_properties, define_property,
 | |
|     prelude::*,
 | |
|     qdev::{DeviceImpl, DeviceState, Property},
 | |
|     qom::ObjectImpl,
 | |
|     vmstate::VMStateDescription,
 | |
|     zeroable::Zeroable,
 | |
| };
 | |
| 
 | |
| // Test that macros can compile.
 | |
| pub static VMSTATE: VMStateDescription = VMStateDescription {
 | |
|     name: c_str!("name").as_ptr(),
 | |
|     unmigratable: true,
 | |
|     ..Zeroable::ZERO
 | |
| };
 | |
| 
 | |
| #[derive(qemu_api_macros::offsets)]
 | |
| #[repr(C)]
 | |
| #[derive(qemu_api_macros::Object)]
 | |
| pub struct DummyState {
 | |
|     parent: DeviceState,
 | |
|     migrate_clock: bool,
 | |
| }
 | |
| 
 | |
| qom_isa!(DummyState: Object, DeviceState);
 | |
| 
 | |
| declare_properties! {
 | |
|     DUMMY_PROPERTIES,
 | |
|         define_property!(
 | |
|             c_str!("migrate-clk"),
 | |
|             DummyState,
 | |
|             migrate_clock,
 | |
|             unsafe { &qdev_prop_bool },
 | |
|             bool
 | |
|         ),
 | |
| }
 | |
| 
 | |
| unsafe impl ObjectType for DummyState {
 | |
|     type Class = <DeviceState as ObjectType>::Class;
 | |
|     const TYPE_NAME: &'static CStr = c_str!("dummy");
 | |
| }
 | |
| 
 | |
| impl ObjectImpl for DummyState {
 | |
|     type ParentType = DeviceState;
 | |
|     const ABSTRACT: bool = false;
 | |
| }
 | |
| 
 | |
| impl DeviceImpl for DummyState {
 | |
|     fn properties() -> &'static [Property] {
 | |
|         &DUMMY_PROPERTIES
 | |
|     }
 | |
|     fn vmsd() -> Option<&'static VMStateDescription> {
 | |
|         Some(&VMSTATE)
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn init_qom() {
 | |
|     static ONCE: BqlCell<bool> = BqlCell::new(false);
 | |
| 
 | |
|     cell::bql_start_test();
 | |
|     if !ONCE.get() {
 | |
|         unsafe {
 | |
|             module_call_init(module_init_type::MODULE_INIT_QOM);
 | |
|         }
 | |
|         ONCE.set(true);
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[test]
 | |
| /// Create and immediately drop an instance.
 | |
| fn test_object_new() {
 | |
|     init_qom();
 | |
|     unsafe {
 | |
|         object_unref(object_new(DummyState::TYPE_NAME.as_ptr()).cast());
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[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:
 | |
| //
 | |
| //    let sbd_ref = p.dynamic_cast::<SysBusDevice>();
 | |
| //
 | |
| // Here I am doing the opposite to check that the resulting type is correct.
 | |
| 
 | |
| #[test]
 | |
| #[allow(clippy::shadow_unrelated)]
 | |
| /// Test casts on shared references.
 | |
| fn test_cast() {
 | |
|     init_qom();
 | |
|     let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() };
 | |
| 
 | |
|     let p_ref: &DummyState = unsafe { &*p };
 | |
|     let obj_ref: &Object = p_ref.upcast();
 | |
|     assert_eq!(addr_of!(*obj_ref), p.cast());
 | |
| 
 | |
|     let sbd_ref: Option<&SysBusDevice> = obj_ref.dynamic_cast();
 | |
|     assert!(sbd_ref.is_none());
 | |
| 
 | |
|     let dev_ref: Option<&DeviceState> = obj_ref.downcast();
 | |
|     assert_eq!(addr_of!(*dev_ref.unwrap()), p.cast());
 | |
| 
 | |
|     // SAFETY: the cast is wrong, but the value is only used for comparison
 | |
|     unsafe {
 | |
|         let sbd_ref: &SysBusDevice = obj_ref.unsafe_cast();
 | |
|         assert_eq!(addr_of!(*sbd_ref), p.cast());
 | |
| 
 | |
|         object_unref(p_ref.as_object_mut_ptr().cast::<c_void>());
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[test]
 | |
| #[allow(clippy::shadow_unrelated)]
 | |
| /// Test casts on mutable references.
 | |
| fn test_cast_mut() {
 | |
|     init_qom();
 | |
|     let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() };
 | |
| 
 | |
|     let p_ref: &mut DummyState = unsafe { &mut *p };
 | |
|     let obj_ref: &mut Object = p_ref.upcast_mut();
 | |
|     assert_eq!(addr_of_mut!(*obj_ref), p.cast());
 | |
| 
 | |
|     let sbd_ref: Result<&mut SysBusDevice, &mut Object> = obj_ref.dynamic_cast_mut();
 | |
|     let obj_ref = sbd_ref.unwrap_err();
 | |
| 
 | |
|     let dev_ref: Result<&mut DeviceState, &mut Object> = obj_ref.downcast_mut();
 | |
|     let dev_ref = dev_ref.unwrap();
 | |
|     assert_eq!(addr_of_mut!(*dev_ref), p.cast());
 | |
| 
 | |
|     // SAFETY: the cast is wrong, but the value is only used for comparison
 | |
|     unsafe {
 | |
|         let sbd_ref: &mut SysBusDevice = obj_ref.unsafe_cast_mut();
 | |
|         assert_eq!(addr_of_mut!(*sbd_ref), p.cast());
 | |
| 
 | |
|         object_unref(p_ref.as_object_mut_ptr().cast::<c_void>());
 | |
|     }
 | |
| }
 |