mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-07-27 20:33:54 -06:00
rust: vmstate: add new type safe implementation
The existing translation of the C macros for vmstate does not make any attempt to type-check vmstate declarations against the struct, so introduce a new system that computes VMStateField based on the actual struct declaration. Macros do not have full access to the type system, therefore a full implementation of this scheme requires a helper trait to analyze the type and produce a VMStateField from it; a macro "vmstate_of!" accepts arguments similar to "offset_of!" and tricks the compiler into looking up the trait for the right type. The patch introduces not just vmstate_of!, but also the slightly too clever enabling macro call_func_with_field!. The particular trick used here was proposed on the users.rust-lang.org forum, so I take no merit and all the blame. Introduce the trait and some functions to access it; the actual implementation comes later. Reviewed-by: Zhao Liu <zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
57f9d9c84a
commit
0d43ddae35
2 changed files with 109 additions and 6 deletions
|
@ -4,13 +4,114 @@
|
|||
|
||||
//! Helper macros to declare migration state for device models.
|
||||
//!
|
||||
//! Some macros are direct equivalents to the C macros declared in
|
||||
//! `include/migration/vmstate.h` while
|
||||
//! [`vmstate_subsections`](crate::vmstate_subsections) and
|
||||
//! [`vmstate_fields`](crate::vmstate_fields) are meant to be used when
|
||||
//! declaring a device model state struct.
|
||||
//! This module includes three families of macros:
|
||||
//!
|
||||
//! * [`vmstate_unused!`](crate::vmstate_unused) and
|
||||
//! [`vmstate_of!`](crate::vmstate_of), which are used to express the
|
||||
//! migration format for a struct. This is based on the [`VMState`] trait,
|
||||
//! which is defined by all migrateable types.
|
||||
//!
|
||||
//! * helper macros to declare a device model state struct, in particular
|
||||
//! [`vmstate_subsections`](crate::vmstate_subsections) and
|
||||
//! [`vmstate_fields`](crate::vmstate_fields).
|
||||
//!
|
||||
//! * direct equivalents to the C macros declared in
|
||||
//! `include/migration/vmstate.h`. These are not type-safe and should not be
|
||||
//! used if the equivalent functionality is available with `vmstate_of!`.
|
||||
|
||||
pub use crate::bindings::VMStateDescription;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
pub use crate::bindings::{VMStateDescription, VMStateField};
|
||||
|
||||
/// This macro is used to call a function with a generic argument bound
|
||||
/// to the type of a field. The function must take a
|
||||
/// [`PhantomData`]`<T>` argument; `T` is the type of
|
||||
/// field `$field` in the `$typ` type.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use qemu_api::call_func_with_field;
|
||||
/// # use core::marker::PhantomData;
|
||||
/// const fn size_of_field<T>(_: PhantomData<T>) -> usize {
|
||||
/// std::mem::size_of::<T>()
|
||||
/// }
|
||||
///
|
||||
/// struct Foo {
|
||||
/// x: u16,
|
||||
/// };
|
||||
/// // calls size_of_field::<u16>()
|
||||
/// assert_eq!(call_func_with_field!(size_of_field, Foo, x), 2);
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! call_func_with_field {
|
||||
// Based on the answer by user steffahn (Frank Steffahn) at
|
||||
// https://users.rust-lang.org/t/inferring-type-of-field/122857
|
||||
// and used under MIT license
|
||||
($func:expr, $typ:ty, $($field:tt).+) => {
|
||||
$func(loop {
|
||||
#![allow(unreachable_code)]
|
||||
const fn phantom__<T>(_: &T) -> ::core::marker::PhantomData<T> { ::core::marker::PhantomData }
|
||||
// Unreachable code is exempt from checks on uninitialized values.
|
||||
// Use that trick to infer the type of this PhantomData.
|
||||
break ::core::marker::PhantomData;
|
||||
break phantom__(&{ let value__: $typ; value__.$($field).+ });
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
/// A trait for types that can be included in a device's migration stream. It
|
||||
/// provides the base contents of a `VMStateField` (minus the name and offset).
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The contents of this trait go straight into structs that are parsed by C
|
||||
/// code and used to introspect into other structs. Be careful.
|
||||
pub unsafe trait VMState {
|
||||
/// The base contents of a `VMStateField` (minus the name and offset) for
|
||||
/// the type that is implementing the trait.
|
||||
const BASE: VMStateField;
|
||||
}
|
||||
|
||||
/// Internal utility function to retrieve a type's `VMStateField`;
|
||||
/// used by [`vmstate_of!`](crate::vmstate_of).
|
||||
pub const fn vmstate_base<T: VMState>(_: PhantomData<T>) -> VMStateField {
|
||||
T::BASE
|
||||
}
|
||||
|
||||
/// Return the `VMStateField` for a field of a struct. The field must be
|
||||
/// visible in the current scope.
|
||||
///
|
||||
/// In order to support other types, the trait `VMState` must be implemented
|
||||
/// for them.
|
||||
#[macro_export]
|
||||
macro_rules! vmstate_of {
|
||||
($struct_name:ty, $field_name:ident $(,)?) => {
|
||||
$crate::bindings::VMStateField {
|
||||
name: ::core::concat!(::core::stringify!($field_name), "\0")
|
||||
.as_bytes()
|
||||
.as_ptr() as *const ::std::os::raw::c_char,
|
||||
offset: $crate::offset_of!($struct_name, $field_name),
|
||||
// Compute most of the VMStateField from the type of the field.
|
||||
..$crate::call_func_with_field!(
|
||||
$crate::vmstate::vmstate_base,
|
||||
$struct_name,
|
||||
$field_name
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Add a couple builder-style methods to VMStateField, allowing
|
||||
// easy derivation of VMStateField constants from other types.
|
||||
impl VMStateField {
|
||||
#[must_use]
|
||||
pub const fn with_version_id(mut self, version_id: i32) -> Self {
|
||||
assert!(version_id >= 0);
|
||||
self.version_id = version_id;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "VMSTATE_UNUSED_BUFFER")]
|
||||
#[macro_export]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue