vtable crate: Some refactoring and simplifications

This commit is contained in:
Olivier Goffart 2020-05-15 21:04:44 +02:00
parent 903fc513de
commit 4ca45ca4da
7 changed files with 97 additions and 146 deletions

View file

@ -3,7 +3,7 @@ pub use sixtyfps_rs_macro::sixtyfps;
/// internal re_exports used by the macro generated /// internal re_exports used by the macro generated
pub mod re_exports { pub mod re_exports {
pub use const_field_offset::FieldOffsets; pub use const_field_offset::FieldOffsets;
pub use corelib::abi::datastructures::{Component, ComponentVTable, ItemTreeNode, ComponentTO}; pub use corelib::abi::datastructures::{Component, ComponentTO, ComponentVTable, ItemTreeNode};
pub use corelib::abi::primitives::{Image, ImageVTable, Rectangle, RectangleVTable}; pub use corelib::abi::primitives::{Image, ImageVTable, Rectangle, RectangleVTable};
pub use gl::sixtyfps_runtime_run_component_with_gl_renderer; pub use gl::sixtyfps_runtime_run_component_with_gl_renderer;
pub use once_cell::sync::Lazy; pub use once_cell::sync::Lazy;

View file

@ -52,7 +52,6 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
let trait_name = Ident::new(&vtable_name[..vtable_name.len() - 6], input.ident.span()); let trait_name = Ident::new(&vtable_name[..vtable_name.len() - 6], input.ident.span());
let to_name = quote::format_ident!("{}TO", trait_name); let to_name = quote::format_ident!("{}TO", trait_name);
let impl_name = quote::format_ident!("{}Impl", trait_name);
let module_name = quote::format_ident!("{}_vtable_mod", trait_name); let module_name = quote::format_ident!("{}_vtable_mod", trait_name);
let ref_name = quote::format_ident!("{}Ref", trait_name); let ref_name = quote::format_ident!("{}Ref", trait_name);
let refmut_name = quote::format_ident!("{}RefMut", trait_name); let refmut_name = quote::format_ident!("{}RefMut", trait_name);
@ -209,10 +208,10 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
})); }));
let self_ty = &param.ty; let self_ty = &param.ty;
let const_or_mut = mutability.map_or_else(|| quote!(const), |x| quote!(#x)); let const_or_mut = mutability.map_or_else(|| quote!(const), |x| quote!(#x));
call_code = Some(quote!(#call_code <#self_ty>::from_inner(*self),)); call_code =
self_call = Some( Some(quote!(#call_code <#self_ty>::from_raw(self.vtable, self.ptr),));
quote!(&#mutability (*(<#self_ty>::get_ptr(&#arg_name).as_ptr() as *#const_or_mut T)),), self_call =
); Some(quote!(&#mutability (*(#arg_name.as_ptr() as *#const_or_mut T)),));
has_self = true; has_self = true;
continue; continue;
} }
@ -271,10 +270,12 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
drop_impl = Some(quote! { drop_impl = Some(quote! {
impl VTableMetaDrop for #vtable_name { impl VTableMetaDrop for #vtable_name {
unsafe fn drop(ptr: #to_name) { unsafe fn drop(ptr: *mut #to_name) {
// Safety: The vtable is valid and inner is a type corresponding to the vtable, // Safety: The vtable is valid and inner is a type corresponding to the vtable,
// which was allocated such that drop is expected. // which was allocated such that drop is expected.
unsafe { (ptr.vtable.as_ref().#ident)(VRefMut::from_inner(ptr)) } unsafe {
let ptr = &*ptr;
(ptr.vtable.as_ref().#ident)(VRefMut::from_raw(ptr.vtable, ptr.ptr)) }
} }
} }
@ -291,7 +292,7 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
generated_to_fn_trait.push(ImplItemMethod { generated_to_fn_trait.push(ImplItemMethod {
attrs: vec![], attrs: vec![],
vis: Visibility::Inherited, vis: Visibility::Public(VisPublic { pub_token: Default::default() }),
defaultness: None, defaultness: None,
sig: sig.clone(), sig: sig.clone(),
block: parse2(if has_self { block: parse2(if has_self {
@ -412,36 +413,28 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
#generated_trait #generated_trait
#generated_trait_assoc_const #generated_trait_assoc_const
struct #impl_name { _private: [u8; 0] } /// Invariant, same as vtable::Inner: vtable and ptr has to be valid and ptr an instance macthcin the vtable
/// This structure is highly unsafe, as it just has pointers. One could call trait functions
/// directly. However, it should not be possible, in safe code, to construct or to obtain a reference
/// to this structure, as it cannot be constructed safely. And none of the safe api allow accessing
/// a reference or a copy of this structure
#[doc(hidden)] #[doc(hidden)]
#[derive(Clone, Copy)]
#[repr(C)] #[repr(C)]
pub struct #to_name { pub struct #to_name {
vtable: core::ptr::NonNull<#vtable_name>, vtable: core::ptr::NonNull<#vtable_name>,
ptr: core::ptr::NonNull<#impl_name>, ptr: core::ptr::NonNull<u8>,
}
impl #to_name {
#(#generated_to_fn_trait)*
pub fn get_vtable(&self) -> &#vtable_name {
unsafe { self.vtable.as_ref() }
}
pub fn as_ptr(&self) -> *const u8 {
self.ptr.as_ptr()
}
} }
impl #trait_name for #to_name { #(#generated_to_fn_trait)* }
unsafe impl VTableMeta for #vtable_name { unsafe impl VTableMeta for #vtable_name {
type Trait = dyn #trait_name;
type VTable = #vtable_name; type VTable = #vtable_name;
type TraitObject = #to_name; type Target = #to_name;
#[inline]
unsafe fn map_to(from: &Self::TraitObject) -> &Self::Trait { from }
#[inline]
unsafe fn map_to_mut(from: &mut Self::TraitObject) -> &mut Self::Trait { from }
#[inline]
unsafe fn get_ptr(from: &Self::TraitObject) -> core::ptr::NonNull<u8> { from.ptr.cast() }
#[inline]
unsafe fn get_vtable(from: &Self::TraitObject) -> &Self::VTable { from.vtable.as_ref() }
#[inline]
unsafe fn from_raw(vtable: core::ptr::NonNull<Self::VTable>, ptr: core::ptr::NonNull<u8>) -> Self::TraitObject
{ #to_name { vtable, ptr : ptr.cast() } }
} }
#drop_impl #drop_impl

View file

@ -4,86 +4,72 @@ use core::ptr::NonNull;
pub use vtable_macro::*; pub use vtable_macro::*;
pub unsafe trait VTableMeta { pub unsafe trait VTableMeta {
/// that's the rust trait. (e.g: `Hello`) /// That's the trait object that implements the functions
type Trait: ?Sized; /// NOTE: the size must be 2*size_of::<usize>
/// that's the vtable struct `HelloVTable` /// and a repr(C) with (vtable, ptr) so it has the same layout as
/// the inner and VBox/VRef/VRefMut
type Target;
/// That's the VTable itself (so most likely Self)
type VTable; type VTable;
/// That's the trait object that implements the trait.
/// NOTE: the size must be 2*size_of<usize>
type TraitObject: Copy;
/// That maps from the tait object from the trait iteself
/// (In other word, return 'to' since 'to' implements trait,
/// but we can't represent that in rust right now, hence these helper)
///
/// Safety: the trait object need to be pointing to valid pointer / vtable
unsafe fn map_to(to: &Self::TraitObject) -> &Self::Trait;
/// Same as map_to, but mutable
unsafe fn map_to_mut(to: &mut Self::TraitObject) -> &mut Self::Trait;
/// Return a raw pointer to the inside of the impl
unsafe fn get_ptr(from: &Self::TraitObject) -> NonNull<u8>;
/// Create a trait object from its raw parts
unsafe fn from_raw(vtable: NonNull<Self::VTable>, ptr: NonNull<u8>) -> Self::TraitObject;
/// return a reference to the vtable
unsafe fn get_vtable(from: &Self::TraitObject) -> &Self::VTable;
} }
pub trait VTableMetaDrop: VTableMeta { pub trait VTableMetaDrop: VTableMeta {
/// Safety: the traitobject need to be pointing to a valid allocated pointer /// Safety: the Target need to be pointing to a valid allocated pointer
unsafe fn drop(ptr: Self::TraitObject); unsafe fn drop(ptr: *mut Self::Target);
} }
// These checks are not enough to ensure that this is not unsafe. #[derive(Copy, Clone)]
fn sanity_checks<T: ?Sized + VTableMeta>() { /// The inner structure of VRef, VRefMut, and VBox.
debug_assert_eq!(core::mem::size_of::<T::TraitObject>(), 2 * core::mem::size_of::<usize>()); ///
/// Invariant: _vtable and _ptr are valid pointer for the lifetime of the container.
/// _ptr is an instance of the object represented by _vtable
#[allow(dead_code)]
struct Inner {
vtable: *const u8,
ptr: *const u8,
}
impl Inner {
/// Transmute a reference to self into a reference to T::Target.
fn deref<T: ?Sized + VTableMeta>(&self) -> *const T::Target {
debug_assert_eq!(core::mem::size_of::<T::Target>(), core::mem::size_of::<Inner>());
self as *const Inner as *const T::Target
}
} }
#[repr(C)] #[repr(C)]
pub struct VBox<T: ?Sized + VTableMetaDrop> { pub struct VBox<T: ?Sized + VTableMetaDrop> {
inner: T::TraitObject, inner: Inner,
phantom: PhantomData<T::Target>,
} }
impl<T: ?Sized + VTableMetaDrop> Deref for VBox<T> { impl<T: ?Sized + VTableMetaDrop> Deref for VBox<T> {
type Target = T::Trait; type Target = T::Target;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
sanity_checks::<T>(); unsafe { &*self.inner.deref::<T>() }
unsafe { T::map_to(&self.inner) }
} }
} }
impl<T: ?Sized + VTableMetaDrop> DerefMut for VBox<T> { impl<T: ?Sized + VTableMetaDrop> DerefMut for VBox<T> {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
sanity_checks::<T>(); unsafe { &mut *(self.inner.deref::<T>() as *mut _) }
unsafe { T::map_to_mut(&mut self.inner) }
} }
} }
impl<T: ?Sized + VTableMetaDrop> Drop for VBox<T> { impl<T: ?Sized + VTableMetaDrop> Drop for VBox<T> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
T::drop(self.inner); T::drop(self.inner.deref::<T>() as *mut _);
} }
} }
} }
impl<T: ?Sized + VTableMetaDrop> VBox<T> { impl<T: ?Sized + VTableMetaDrop> VBox<T> {
pub unsafe fn from_inner(inner: T::TraitObject) -> Self {
Self { inner }
}
pub unsafe fn inner(x: &Self) -> T::TraitObject {
x.inner
}
pub unsafe fn get_ptr(x: &Self) -> NonNull<u8> {
T::get_ptr(&x.inner)
}
pub unsafe fn from_raw(vtable: NonNull<T::VTable>, ptr: NonNull<u8>) -> Self { pub unsafe fn from_raw(vtable: NonNull<T::VTable>, ptr: NonNull<u8>) -> Self {
Self { inner: T::from_raw(vtable, ptr) } Self {
} inner: Inner { vtable: vtable.cast().as_ptr(), ptr: ptr.cast().as_ptr() },
pub fn get_vtable(&self) -> &T::VTable { phantom: PhantomData,
unsafe { T::get_vtable(&self.inner) } }
} }
pub fn borrow<'b>(&'b self) -> VRef<'b, T> { pub fn borrow<'b>(&'b self) -> VRef<'b, T> {
unsafe { VRef::from_inner(self.inner) } unsafe { VRef::from_inner(self.inner) }
@ -94,8 +80,8 @@ impl<T: ?Sized + VTableMetaDrop> VBox<T> {
} }
pub struct VRef<'a, T: ?Sized + VTableMeta> { pub struct VRef<'a, T: ?Sized + VTableMeta> {
inner: T::TraitObject, inner: Inner,
_phantom: PhantomData<&'a T::Trait>, phantom: PhantomData<&'a T::Target>,
} }
// Need to implement manually otheriwse it is not implemented if T do not implement Copy / Clone // Need to implement manually otheriwse it is not implemented if T do not implement Copy / Clone
@ -103,80 +89,65 @@ impl<'a, T: ?Sized + VTableMeta> Copy for VRef<'a, T> {}
impl<'a, T: ?Sized + VTableMeta> Clone for VRef<'a, T> { impl<'a, T: ?Sized + VTableMeta> Clone for VRef<'a, T> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { inner: self.inner, _phantom: self._phantom } Self { inner: self.inner, phantom: PhantomData }
} }
} }
impl<'a, T: ?Sized + VTableMeta> Deref for VRef<'a, T> { impl<'a, T: ?Sized + VTableMeta> Deref for VRef<'a, T> {
type Target = T::Trait; type Target = T::Target;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
sanity_checks::<T>(); unsafe { &*self.inner.deref::<T>() }
unsafe { T::map_to(&self.inner) }
} }
} }
impl<'a, T: ?Sized + VTableMeta> VRef<'a, T> { impl<'a, T: ?Sized + VTableMeta> VRef<'a, T> {
pub unsafe fn from_inner(inner: T::TraitObject) -> Self { unsafe fn from_inner(inner: Inner) -> Self {
Self { inner, _phantom: PhantomData } Self { inner, phantom: PhantomData }
}
pub unsafe fn inner(x: &Self) -> T::TraitObject {
x.inner
}
pub unsafe fn get_ptr(x: &Self) -> NonNull<u8> {
T::get_ptr(&x.inner)
} }
pub unsafe fn from_raw(vtable: NonNull<T::VTable>, ptr: NonNull<u8>) -> Self { pub unsafe fn from_raw(vtable: NonNull<T::VTable>, ptr: NonNull<u8>) -> Self {
Self { inner: T::from_raw(vtable, ptr), _phantom: PhantomData } Self {
} inner: Inner { vtable: vtable.cast().as_ptr(), ptr: ptr.cast().as_ptr() },
pub fn get_vtable(&self) -> &T::VTable { phantom: PhantomData,
unsafe { T::get_vtable(&self.inner) } }
} }
} }
pub struct VRefMut<'a, T: ?Sized + VTableMeta> { pub struct VRefMut<'a, T: ?Sized + VTableMeta> {
inner: T::TraitObject, inner: Inner,
_phantom: PhantomData<&'a mut T::Trait>, phantom: PhantomData<&'a mut T::Target>,
} }
impl<'a, T: ?Sized + VTableMeta> Deref for VRefMut<'a, T> { impl<'a, T: ?Sized + VTableMeta> Deref for VRefMut<'a, T> {
type Target = T::Trait; type Target = T::Target;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
sanity_checks::<T>(); unsafe { &*self.inner.deref::<T>() }
unsafe { T::map_to(&self.inner) }
} }
} }
impl<'a, T: ?Sized + VTableMeta> DerefMut for VRefMut<'a, T> { impl<'a, T: ?Sized + VTableMeta> DerefMut for VRefMut<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
sanity_checks::<T>(); unsafe { &mut *(self.inner.deref::<T>() as *mut _) }
unsafe { T::map_to_mut(&mut self.inner) }
} }
} }
impl<'a, T: ?Sized + VTableMeta> VRefMut<'a, T> { impl<'a, T: ?Sized + VTableMeta> VRefMut<'a, T> {
pub unsafe fn from_inner(inner: T::TraitObject) -> Self { unsafe fn from_inner(inner: Inner) -> Self {
Self { inner, _phantom: PhantomData } Self { inner, phantom: PhantomData }
}
pub unsafe fn inner(x: &Self) -> T::TraitObject {
x.inner
}
pub unsafe fn get_ptr(x: &Self) -> NonNull<u8> {
T::get_ptr(&x.inner)
}
pub fn borrow<'b>(&'b self) -> VRef<'b, T> {
unsafe { VRef::from_inner(VRefMut::inner(self)) }
}
pub fn borrow_mut<'b>(&'b mut self) -> VRefMut<'b, T> {
unsafe { VRefMut::from_inner(VRefMut::inner(self)) }
}
pub fn into_ref(self) -> VRef<'a, T> {
unsafe { VRef::from_inner(VRefMut::inner(&self)) }
} }
pub unsafe fn from_raw(vtable: NonNull<T::VTable>, ptr: NonNull<u8>) -> Self { pub unsafe fn from_raw(vtable: NonNull<T::VTable>, ptr: NonNull<u8>) -> Self {
Self { inner: T::from_raw(vtable, ptr), _phantom: PhantomData } Self {
inner: Inner { vtable: vtable.cast().as_ptr(), ptr: ptr.cast().as_ptr() },
phantom: PhantomData,
}
} }
pub fn get_vtable(&self) -> &T::VTable { pub fn borrow<'b>(&'b self) -> VRef<'b, T> {
unsafe { T::get_vtable(&self.inner) } unsafe { VRef::from_inner(self.inner) }
}
pub fn borrow_mut<'b>(&'b mut self) -> VRefMut<'b, T> {
unsafe { VRefMut::from_inner(self.inner) }
}
pub fn into_ref(self) -> VRef<'a, T> {
unsafe { VRef::from_inner(self.inner) }
} }
} }

View file

@ -26,7 +26,6 @@ impl Hello for SomeStruct {
self.0 self.0
} }
fn construct(init: u32) -> Self { fn construct(init: u32) -> Self {
println!("calling Construct {}", init); println!("calling Construct {}", init);
Self(init) Self(init)
@ -40,7 +39,7 @@ impl HelloConsts for SomeStruct {
const CONSTANT: usize = 88; const CONSTANT: usize = 88;
} }
static SOME_STRUCT_TYPE : HelloVTable = HelloVTable_static!(SomeStruct); static SOME_STRUCT_TYPE: HelloVTable = HelloVTable_static!(SomeStruct);
#[test] #[test]
fn test() { fn test() {
@ -53,5 +52,3 @@ fn test() {
assert_eq!(bx.foo(2), 97); assert_eq!(bx.foo(2), 97);
assert_eq!(bx.get_vtable().CONSTANT, 88); assert_eq!(bx.get_vtable().CONSTANT, 88);
} }

View file

@ -121,14 +121,14 @@ pub type MouseEvent = ();
pub fn cached_rendering_data(item: VRef<'_, ItemVTable>) -> &CachedRenderingData { pub fn cached_rendering_data(item: VRef<'_, ItemVTable>) -> &CachedRenderingData {
unsafe { unsafe {
&*(VRef::get_ptr(&item).as_ptr().offset(item.get_vtable().cached_rendering_data_offset) &*(item.as_ptr().offset(item.get_vtable().cached_rendering_data_offset)
as *const CachedRenderingData) as *const CachedRenderingData)
} }
} }
pub fn cached_rendering_data_mut(item: VRefMut<'_, ItemVTable>) -> &mut CachedRenderingData { pub fn cached_rendering_data_mut(item: VRefMut<'_, ItemVTable>) -> &mut CachedRenderingData {
unsafe { unsafe {
&mut *(VRefMut::get_ptr(&item).as_ptr().offset(item.get_vtable().cached_rendering_data_offset) &mut *(item.as_ptr().offset(item.get_vtable().cached_rendering_data_offset)
as *mut CachedRenderingData) as *mut CachedRenderingData)
} }
} }
@ -156,9 +156,7 @@ fn visit_internal<State>(
let item = unsafe { let item = unsafe {
ItemRef::from_raw( ItemRef::from_raw(
NonNull::new_unchecked(*vtable as *mut _), NonNull::new_unchecked(*vtable as *mut _),
NonNull::new_unchecked( NonNull::new_unchecked(component.as_ptr().offset(*offset) as *mut _),
(VRef::get_ptr(&component).as_ptr()).offset(*offset) as *mut _
),
) )
}; };
let state = visitor(item, state); let state = visitor(item, state);
@ -191,8 +189,7 @@ fn visit_internal_mut<State>(
ItemRefMut::from_raw( ItemRefMut::from_raw(
NonNull::new_unchecked(*vtable as *mut _), NonNull::new_unchecked(*vtable as *mut _),
NonNull::new_unchecked( NonNull::new_unchecked(
(VRefMut::get_ptr(&component).as_ptr() as *mut u8).offset(*offset) (component.as_ptr() as *mut u8).offset(*offset) as *mut _
as *mut _,
), ),
) )
}; };
@ -296,8 +293,6 @@ pub static QT_BUTTON_VTABLE: ItemVTable = ItemVTable {
}; };
*/ */
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
#[no_mangle] #[no_mangle]
pub static RectangleVTable: ItemVTable = ItemVTable_static!(crate::abi::primitives::Rectangle); pub static RectangleVTable: ItemVTable = ItemVTable_static!(crate::abi::primitives::Rectangle);

View file

@ -80,5 +80,4 @@ impl ItemConsts for Image {
Image::field_offsets().cached_rendering_data as isize; Image::field_offsets().cached_rendering_data as isize;
} }
pub use super::datastructures::{ImageVTable, RectangleVTable}; pub use super::datastructures::{ImageVTable, RectangleVTable};

View file

@ -69,14 +69,10 @@ struct MyComponentType {
it: Vec<corelib::abi::datastructures::ItemTreeNode>, it: Vec<corelib::abi::datastructures::ItemTreeNode>,
} }
extern "C" fn item_tree( extern "C" fn item_tree(r: ComponentRef<'_>) -> *const corelib::abi::datastructures::ItemTreeNode {
r: ComponentRef<'_>,
) -> *const corelib::abi::datastructures::ItemTreeNode {
// FIXME! unsafe is not correct here, as the ComponentVTable might not be a MyComponentType // FIXME! unsafe is not correct here, as the ComponentVTable might not be a MyComponentType
// (one can safely take a copy of the vtable and call the create function to get a box) // (one can safely take a copy of the vtable and call the create function to get a box)
unsafe { unsafe { (*(r.get_vtable() as *const ComponentVTable as *const MyComponentType)).it.as_ptr() }
(*(ComponentRef::get_vtable(&r) as *const ComponentVTable as *const MyComponentType)).it.as_ptr()
}
} }
struct RuntimeTypeInfo { struct RuntimeTypeInfo {