mirror of
https://github.com/slint-ui/slint.git
synced 2025-09-28 12:54:45 +00:00
More work on the vtable crate
This commit is contained in:
parent
aa27fceb8c
commit
1ced4224b5
5 changed files with 338 additions and 211 deletions
18
api/sixtyfps-cpp/include/vtable.h
Normal file
18
api/sixtyfps-cpp/include/vtable.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct VBox {
|
||||||
|
T *vtable;
|
||||||
|
void *instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct VRef {
|
||||||
|
T *vtable;
|
||||||
|
void *instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct VRefMut {
|
||||||
|
T *vtable;
|
||||||
|
void *instance;
|
||||||
|
}
|
|
@ -11,6 +11,23 @@ use quote::quote;
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
use syn::*;
|
use syn::*;
|
||||||
|
|
||||||
|
/// Returns true if the type `ty` is "Container<Containee>"
|
||||||
|
fn match_generic_type(ty: &Type, container: &str, containee: &Ident) -> bool {
|
||||||
|
if let Type::Path(pat) = ty {
|
||||||
|
if let Some(seg) = pat.path.segments.last() {
|
||||||
|
if seg.ident != container {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if let PathArguments::AngleBracketed(args) = &seg.arguments {
|
||||||
|
if let Some(GenericArgument::Type(Type::Path(arg))) = args.args.last() {
|
||||||
|
return Some(containee) == arg.path.get_ident();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
let mut input = parse_macro_input!(item as ItemStruct);
|
let mut input = parse_macro_input!(item as ItemStruct);
|
||||||
|
@ -36,17 +53,11 @@ 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 impl_name = quote::format_ident!("{}Impl", trait_name);
|
||||||
|
let type_name = quote::format_ident!("{}Type", trait_name);
|
||||||
let module_name = quote::format_ident!("{}_vtable_mod", trait_name);
|
let module_name = quote::format_ident!("{}_vtable_mod", trait_name);
|
||||||
let box_name = quote::format_ident!("{}Box", trait_name);
|
|
||||||
let ref_name = quote::format_ident!("{}Ref", trait_name);
|
|
||||||
let refmut_name = quote::format_ident!("{}RefMut", trait_name);
|
|
||||||
let vtable_name = input.ident.clone();
|
let vtable_name = input.ident.clone();
|
||||||
|
|
||||||
let ref_doc = format!("This is an equivalent to a `&'a dyn {}`", trait_name);
|
let mut drop_impl = None;
|
||||||
let refmut_doc = format!("This is an equivalent to a `&'a mut dyn {}`", trait_name);
|
|
||||||
let box_doc = format!("This is an equivalent to a `Box<dyn {}>`", trait_name);
|
|
||||||
|
|
||||||
let mut box_impl = None;
|
|
||||||
|
|
||||||
let mut generated_trait = ItemTrait {
|
let mut generated_trait = ItemTrait {
|
||||||
attrs: input
|
attrs: input
|
||||||
|
@ -68,8 +79,7 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut generated_to_fn_trait = vec![];
|
let mut generated_to_fn_trait = vec![];
|
||||||
let mut generated_to_fn_assoc = vec![];
|
let mut generated_type_assoc_fn = vec![];
|
||||||
let mut generated_constructor = vec![];
|
|
||||||
let mut vtable_ctor = vec![];
|
let mut vtable_ctor = vec![];
|
||||||
|
|
||||||
for field in &mut fields.named {
|
for field in &mut fields.named {
|
||||||
|
@ -98,6 +108,10 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
let mut call_code = None;
|
let mut call_code = None;
|
||||||
let mut self_call = None;
|
let mut self_call = None;
|
||||||
let mut forward_code = None;
|
let mut forward_code = None;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct SelfInfo {}
|
||||||
|
|
||||||
let mut has_self = false;
|
let mut has_self = false;
|
||||||
|
|
||||||
for param in &f.inputs {
|
for param in &f.inputs {
|
||||||
|
@ -114,51 +128,58 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
});
|
});
|
||||||
sig_extern.inputs.push(typed_arg.clone());
|
sig_extern.inputs.push(typed_arg.clone());
|
||||||
|
|
||||||
match ¶m.ty {
|
// check for the vtable
|
||||||
Type::Ptr(TypePtr { mutability, elem, .. })
|
if let Type::Ptr(TypePtr { mutability, elem, .. })
|
||||||
| Type::Reference(TypeReference { mutability, elem, .. }) => {
|
| Type::Reference(TypeReference { mutability, elem, .. }) = ¶m.ty
|
||||||
if let Type::Path(p) = &**elem {
|
{
|
||||||
if let Some(pointer_to) = p.path.get_ident() {
|
if let Type::Path(p) = &**elem {
|
||||||
if pointer_to == &vtable_name {
|
if let Some(pointer_to) = p.path.get_ident() {
|
||||||
if mutability.is_some() {
|
if pointer_to == &vtable_name {
|
||||||
return Error::new(p.span(), "VTable cannot be mutable")
|
if mutability.is_some() {
|
||||||
.to_compile_error()
|
return Error::new(p.span(), "VTable cannot be mutable")
|
||||||
.into();
|
|
||||||
}
|
|
||||||
if call_code.is_some() || sig.inputs.len() > 0 {
|
|
||||||
return Error::new(
|
|
||||||
p.span(),
|
|
||||||
"VTable pointer need to be the first",
|
|
||||||
)
|
|
||||||
.to_compile_error()
|
.to_compile_error()
|
||||||
.into();
|
.into();
|
||||||
}
|
|
||||||
call_code = Some(quote!(vtable.as_ptr(),));
|
|
||||||
continue;
|
|
||||||
} else if pointer_to == &impl_name {
|
|
||||||
if sig.inputs.len() > 0 {
|
|
||||||
return Error::new(p.span(), "Impl pointer need to be the first (with the exception of VTable)").to_compile_error().into();
|
|
||||||
}
|
|
||||||
sig.inputs.push(FnArg::Receiver(Receiver {
|
|
||||||
attrs: param.attrs.clone(),
|
|
||||||
reference: Some(Default::default()),
|
|
||||||
mutability: mutability.clone(),
|
|
||||||
self_token: Default::default(),
|
|
||||||
}));
|
|
||||||
call_code = Some(quote!(#call_code ptr.as_ptr(),));
|
|
||||||
let const_or_mut = mutability
|
|
||||||
.map(|x| quote!(#x))
|
|
||||||
.unwrap_or_else(|| quote!(const));
|
|
||||||
self_call = Some(
|
|
||||||
quote!(&#mutability (*(#arg_name as *#const_or_mut T)), ),
|
|
||||||
);
|
|
||||||
has_self = true;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
if call_code.is_some() || sig.inputs.len() > 0 {
|
||||||
|
return Error::new(
|
||||||
|
p.span(),
|
||||||
|
"VTable pointer need to be the first",
|
||||||
|
)
|
||||||
|
.to_compile_error()
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
call_code = Some(quote!(self.vtable.as_ptr(),));
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
}
|
||||||
|
|
||||||
|
// check for self
|
||||||
|
if let (true, mutability) = if match_generic_type(¶m.ty, "VRef", &vtable_name) {
|
||||||
|
(true, None)
|
||||||
|
} else if match_generic_type(¶m.ty, "VRefMut", &vtable_name) {
|
||||||
|
(true, Some(Default::default()))
|
||||||
|
} else {
|
||||||
|
(false, None)
|
||||||
|
} {
|
||||||
|
if sig.inputs.len() > 0 {
|
||||||
|
return Error::new(param.span(), "Self pointer need to be the first")
|
||||||
|
.to_compile_error()
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
sig.inputs.push(FnArg::Receiver(Receiver {
|
||||||
|
attrs: param.attrs.clone(),
|
||||||
|
reference: Some(Default::default()),
|
||||||
|
mutability,
|
||||||
|
self_token: Default::default(),
|
||||||
|
}));
|
||||||
|
let self_ty = ¶m.ty;
|
||||||
|
let const_or_mut = mutability.map_or_else(|| quote!(const), |x| quote!(#x));
|
||||||
|
call_code = Some(quote!(#call_code <#self_ty>::from_inner(*self),));
|
||||||
|
self_call = Some(quote!(&#mutability (*(<#self_ty>::inner(&#arg_name).ptr.as_ptr() as *#const_or_mut T)),));
|
||||||
|
has_self = true;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
sig.inputs.push(typed_arg);
|
sig.inputs.push(typed_arg);
|
||||||
call_code = Some(quote!(#call_code #arg_name,));
|
call_code = Some(quote!(#call_code #arg_name,));
|
||||||
|
@ -175,10 +196,8 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
} else {
|
} else {
|
||||||
f.abi = sig_extern.abi.clone();
|
f.abi = sig_extern.abi.clone();
|
||||||
}
|
}
|
||||||
// Remove pub, if any
|
// The vtable can only be accessed in unsafe code, so it is ok if all its fields are Public
|
||||||
field.vis = Visibility::Inherited;
|
field.vis = Visibility::Public(VisPublic { pub_token: Default::default() });
|
||||||
// FIXME!!!
|
|
||||||
field.vis = Visibility::Public(VisPublic{ pub_token: Default::default() });
|
|
||||||
|
|
||||||
let mut wrap_trait_call = None;
|
let mut wrap_trait_call = None;
|
||||||
if !has_self {
|
if !has_self {
|
||||||
|
@ -186,27 +205,20 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
where_clause: Some(parse_str("where Self : Sized").unwrap()),
|
where_clause: Some(parse_str("where Self : Sized").unwrap()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Check if this is a constructor functions
|
||||||
if let ReturnType::Type(_, ret) = &f.output {
|
if let ReturnType::Type(_, ret) = &f.output {
|
||||||
if let Type::Path(ret) = &**ret {
|
if match_generic_type(&**ret, "VBox", &vtable_name) {
|
||||||
if let Some(seg) = ret.path.segments.last() {
|
// Change VBox<VTable> to Self
|
||||||
if let PathArguments::AngleBracketed(args) = &seg.arguments {
|
sig.output = parse_str("-> Self").unwrap();
|
||||||
if let Some(GenericArgument::Type(Type::Path(arg))) =
|
wrap_trait_call = Some(quote! {
|
||||||
args.args.first()
|
let wrap_trait_call = |x| {
|
||||||
{
|
// Put the object on the heap and get a pointer to it
|
||||||
if let Some(arg) = arg.path.get_ident() {
|
let ptr = core::ptr::NonNull::from(Box::leak(Box::new(x)));
|
||||||
// that's quite a lot of if let to get the argument of the type
|
VBox::<#vtable_name>::from_inner(#to_name { vtable, ptr : ptr.cast() })
|
||||||
if seg.ident == "Box" && arg == &impl_name {
|
};
|
||||||
// Consider this is a constructor, so change Box<Self> to Self
|
wrap_trait_call
|
||||||
sig.output = parse_str("-> Self").unwrap();
|
});
|
||||||
wrap_trait_call = Some(quote! {
|
|
||||||
let wrap_trait_call = |x| Box::from_raw(Box::into_raw(Box::new(x)) as *mut #impl_name);
|
|
||||||
wrap_trait_call
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,46 +233,12 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
#ident::<T>
|
#ident::<T>
|
||||||
},));
|
},));
|
||||||
|
|
||||||
box_impl = Some(quote! {
|
drop_impl = Some(quote! {
|
||||||
#[doc = #box_doc]
|
impl VTableMetaDrop for #vtable_name {
|
||||||
pub struct #box_name {
|
unsafe fn drop(ptr: #to_name) {
|
||||||
inner: #to_name,
|
|
||||||
}
|
|
||||||
impl #box_name {
|
|
||||||
/// Construct the box from raw pointer of a vtable and a corresponding pointer
|
|
||||||
pub unsafe fn from_raw(
|
|
||||||
vtable: core::ptr::NonNull<#vtable_name>,
|
|
||||||
ptr: core::ptr::NonNull<#impl_name>,
|
|
||||||
) -> Self {
|
|
||||||
Self{inner: #to_name{vtable, ptr}}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*pub fn vtable(&self) -> & #vtable_name {
|
|
||||||
unsafe { self.inner.vtable.as_ref() }
|
|
||||||
}*/
|
|
||||||
|
|
||||||
pub fn as_ptr(&self) -> *mut #impl_name {
|
|
||||||
self.inner.ptr.as_ptr()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl core::ops::Deref for #box_name {
|
|
||||||
type Target = dyn #trait_name;
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.inner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl core::ops::DerefMut for #box_name {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.inner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl core::ops::Drop for #box_name {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
#[allow(unused)]
|
|
||||||
let (vtable, ptr) = (&self.inner.vtable, &self.inner.ptr);
|
|
||||||
// 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 { (vtable.as_ref().#ident)(#call_code) }
|
unsafe { (ptr.vtable.as_ref().#ident)(VRefMut::from_inner(ptr)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,10 +261,8 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
block: parse(
|
block: parse(
|
||||||
if has_self {
|
if has_self {
|
||||||
quote!({
|
quote!({
|
||||||
#[allow(unused)]
|
|
||||||
let (vtable, ptr) = (&self.vtable, &self.ptr);
|
|
||||||
// Safety: this rely on the vtable being valid, and the ptr being a valid instance for this vtable
|
// Safety: this rely on the vtable being valid, and the ptr being a valid instance for this vtable
|
||||||
unsafe { (vtable.as_ref().#ident)(#call_code) }
|
unsafe { (self.vtable.as_ref().#ident)(#call_code) }
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// This should never happen: nobody should be able to access the Trait Object directly.
|
// This should never happen: nobody should be able to access the Trait Object directly.
|
||||||
|
@ -307,57 +283,45 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
self_token: Default::default(),
|
self_token: Default::default(),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
if wrap_trait_call.is_some() {
|
sig.output = sig_extern.output.clone();
|
||||||
sig.output = parse(quote!(-> #box_name).into()).unwrap();
|
generated_type_assoc_fn.push(ImplItemMethod {
|
||||||
generated_constructor.push(ImplItemMethod {
|
attrs: vec![],
|
||||||
attrs: vec![],
|
vis: generated_trait.vis.clone(),
|
||||||
vis: generated_trait.vis.clone(),
|
defaultness: None,
|
||||||
defaultness: None,
|
sig,
|
||||||
sig,
|
block: parse(
|
||||||
block: parse(
|
quote!({
|
||||||
quote!({
|
#[allow(unused_parens)]
|
||||||
// Safety: this rely on the vtable being valid, and the ptr being a valid instance for this vtable
|
let vtable = self.vtable;
|
||||||
unsafe {
|
// Safety: this rely on the vtable being valid, and the ptr being a valid instance for this vtable
|
||||||
#[allow(unused)]
|
unsafe { #wrap_trait_call((vtable.as_ref().#ident)(#call_code)) }
|
||||||
let vtable = core::ptr::NonNull::from(self);
|
})
|
||||||
#box_name::from_raw(vtable, std::ptr::NonNull::from(Box::leak((self.#ident)(#call_code))))
|
.into(),
|
||||||
}
|
)
|
||||||
})
|
.unwrap(),
|
||||||
.into(),
|
});
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
generated_to_fn_assoc.push(ImplItemMethod {
|
|
||||||
attrs: vec![],
|
|
||||||
vis: generated_trait.vis.clone(),
|
|
||||||
defaultness: None,
|
|
||||||
sig,
|
|
||||||
block: parse(
|
|
||||||
quote!({
|
|
||||||
#[allow(unused_parens)]
|
|
||||||
#[allow(unused)]
|
|
||||||
let vtable = core::ptr::NonNull::from(self);
|
|
||||||
// Safety: this rely on the vtable being valid, and the ptr being a valid instance for this vtable
|
|
||||||
unsafe { #wrap_trait_call((vtable.as_ref().#ident)(#call_code)) }
|
|
||||||
})
|
|
||||||
.into(),
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vtable_ctor.push(quote!(#ident: {
|
vtable_ctor.push(quote!(#ident: {
|
||||||
#sig_extern {
|
#sig_extern {
|
||||||
#[allow(unused_parens)]
|
#[allow(unused_parens)]
|
||||||
// This is safe since the self must be a instance of our type
|
// This is safe since the self must be a instance of our type
|
||||||
unsafe {
|
unsafe {
|
||||||
#wrap_trait_call(T::#ident(#self_call #forward_code))
|
#[allow(unused)]
|
||||||
|
let vtable = core::ptr::NonNull::new_unchecked(_0 as *mut #vtable_name);
|
||||||
|
#wrap_trait_call(T::#ident(#self_call #forward_code))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
#ident::<T>
|
||||||
#ident::<T>
|
},));
|
||||||
},));
|
} else {
|
||||||
|
vtable_ctor.push(quote!(#ident: {
|
||||||
|
#sig_extern {
|
||||||
|
// This is safe since the self must be a instance of our type
|
||||||
|
unsafe { T::#ident(#self_call #forward_code) }
|
||||||
|
}
|
||||||
|
#ident::<T>
|
||||||
|
},));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return Error::new(field.span(), "member must only be functions")
|
return Error::new(field.span(), "member must only be functions")
|
||||||
.to_compile_error()
|
.to_compile_error()
|
||||||
|
@ -369,13 +333,15 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
input.vis = Visibility::Public(VisPublic { pub_token: Default::default() });
|
input.vis = Visibility::Public(VisPublic { pub_token: Default::default() });
|
||||||
|
|
||||||
/*let (fields_name, fields_type): (Vec<_>, Vec<_>) =
|
/*let (fields_name, fields_type): (Vec<_>, Vec<_>) =
|
||||||
fields.named.iter().map(|f| (f.ident.clone().unwrap(), f.ty.clone())).unzip();*/
|
fields.named.iter().map(|f| (f.ident.clone().unwrap(), f.ty.clone())).unzip();*/
|
||||||
|
|
||||||
let result = quote!(
|
let result = quote!(
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
/// This private module is generated by the `vtable` macro
|
/// This private module is generated by the `vtable` macro
|
||||||
mod #module_name {
|
mod #module_name {
|
||||||
|
#[allow(unused)]
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use ::vtable::*;
|
||||||
#input
|
#input
|
||||||
|
|
||||||
impl #vtable_name {
|
impl #vtable_name {
|
||||||
|
@ -385,60 +351,49 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
#(#vtable_ctor)*
|
#(#vtable_ctor)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*pub unsafe fn from_raw_data(#(#fields_name : #fields_type),*) -> Self {
|
|
||||||
Self {
|
|
||||||
#(#fields_name),*
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
#(#generated_constructor)*
|
|
||||||
#(#generated_to_fn_assoc)*
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#generated_trait
|
#generated_trait
|
||||||
pub struct #impl_name { _private: [u8; 0] }
|
struct #impl_name { _private: [u8; 0] }
|
||||||
|
|
||||||
|
/// 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)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
struct #to_name {
|
#[repr(C)]
|
||||||
|
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<#impl_name>,
|
||||||
}
|
}
|
||||||
impl #trait_name for #to_name { #(#generated_to_fn_trait)* }
|
impl #trait_name for #to_name { #(#generated_to_fn_trait)* }
|
||||||
|
|
||||||
#[doc = #ref_doc]
|
#[repr(transparent)]
|
||||||
#[derive(Clone, Copy)]
|
/// Safe wrapper around a VTable.
|
||||||
pub struct #ref_name<'a> {
|
pub struct #type_name {
|
||||||
inner: #to_name,
|
vtable: core::ptr::NonNull<#vtable_name>
|
||||||
_phantom: core::marker::PhantomData<&'a #impl_name>,
|
|
||||||
}
|
}
|
||||||
impl<'a> core::ops::Deref for #ref_name<'a> {
|
impl #type_name {
|
||||||
type Target = dyn #trait_name;
|
pub unsafe fn from_raw(vtable: core::ptr::NonNull<#vtable_name>) -> Self {
|
||||||
fn deref(&self) -> &Self::Target {
|
Self { vtable }
|
||||||
&self.inner
|
}
|
||||||
}
|
#(#generated_type_assoc_fn)*
|
||||||
|
}
|
||||||
|
unsafe impl VTableMeta for #vtable_name {
|
||||||
|
type Trait = dyn #trait_name;
|
||||||
|
type VTable = #vtable_name;
|
||||||
|
type TraitObject = #to_name;
|
||||||
|
unsafe fn map_to(to: &Self::TraitObject) -> &Self::Trait { to }
|
||||||
|
unsafe fn map_to_mut(to: &mut Self::TraitObject) -> &mut Self::Trait { to }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc = #refmut_doc]
|
#drop_impl
|
||||||
pub struct #refmut_name<'a> {
|
|
||||||
inner: #to_name,
|
|
||||||
_phantom: core::marker::PhantomData<&'a *mut #impl_name>,
|
|
||||||
}
|
|
||||||
impl<'a> core::ops::Deref for #refmut_name<'a> {
|
|
||||||
type Target = dyn #trait_name;
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.inner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'a> core::ops::DerefMut for #refmut_name<'a> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.inner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#box_impl
|
|
||||||
}
|
}
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
#vis use #module_name::*;
|
#vis use #module_name::*;
|
||||||
);
|
);
|
||||||
// println!("{}", result);
|
println!("{}", result);
|
||||||
result.into()
|
result.into()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,140 @@
|
||||||
|
use core::ops::{Deref, DerefMut, Drop};
|
||||||
|
//use core::ptr::NonNull;
|
||||||
pub use vtable_macro::vtable;
|
pub use vtable_macro::vtable;
|
||||||
|
|
||||||
|
pub unsafe trait VTableMeta {
|
||||||
|
/// that's the rust trait. (e.g: `Hello`)
|
||||||
|
type Trait: ?Sized;
|
||||||
|
/// that's the vtable struct `HelloVTable`
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait VTableMetaDrop: VTableMeta {
|
||||||
|
/// Safety: the traitobject need to be pointing to a valid allocated pointer
|
||||||
|
unsafe fn drop(ptr: Self::TraitObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
// These checks are not enough to ensure that this is not unsafe.
|
||||||
|
fn sanity_checks<T: ?Sized + VTableMeta>() {
|
||||||
|
debug_assert_eq!(core::mem::size_of::<T::TraitObject>(), 2 * core::mem::size_of::<usize>());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct VBox<T: ?Sized + VTableMetaDrop> {
|
||||||
|
inner: T::TraitObject,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + VTableMetaDrop> Deref for VBox<T> {
|
||||||
|
type Target = T::Trait;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
sanity_checks::<T>();
|
||||||
|
unsafe { T::map_to(&self.inner) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: ?Sized + VTableMetaDrop> DerefMut for VBox<T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
sanity_checks::<T>();
|
||||||
|
unsafe { T::map_to_mut(&mut self.inner) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + VTableMetaDrop> Drop for VBox<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
T::drop(self.inner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
impl<T: ?Sized + VTableMeta> VBox<T> {
|
||||||
|
/// Construct the box from raw pointer of a vtable and a corresponding pointer
|
||||||
|
pub unsafe fn from_inner(
|
||||||
|
vtable: core::ptr::NonNull<#vtable_name>,
|
||||||
|
ptr: core::ptr::NonNull<#impl_name>,
|
||||||
|
) -> Self {
|
||||||
|
Self{inner: #to_name{vtable, ptr}}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*pub fn vtable(&self) -> & #vtable_name {
|
||||||
|
unsafe { self.inner.vtable.as_ref() }
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/* pub fn as_ptr(&self) -> *mut #impl_name {
|
||||||
|
self.inner.ptr.as_ptr()
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct VRef<'a, T: ?Sized + VTableMeta> {
|
||||||
|
inner: T::TraitObject,
|
||||||
|
_phantom: core::marker::PhantomData<&'a T::Trait>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: ?Sized + VTableMeta> Deref for VRef<'a, T> {
|
||||||
|
type Target = T::Trait;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
sanity_checks::<T>();
|
||||||
|
unsafe { T::map_to(&self.inner) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: ?Sized + VTableMeta> VRef<'a, T> {
|
||||||
|
pub unsafe fn from_inner(inner: T::TraitObject) -> Self {
|
||||||
|
Self { inner, _phantom: core::marker::PhantomData }
|
||||||
|
}
|
||||||
|
pub unsafe fn inner(x: &Self) -> T::TraitObject {
|
||||||
|
x.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct VRefMut<'a, T: ?Sized + VTableMeta> {
|
||||||
|
inner: T::TraitObject,
|
||||||
|
_phantom: core::marker::PhantomData<&'a mut T::Trait>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: ?Sized + VTableMeta> Deref for VRefMut<'a, T> {
|
||||||
|
type Target = T::Trait;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
sanity_checks::<T>();
|
||||||
|
unsafe { T::map_to(&self.inner) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: ?Sized + VTableMeta> DerefMut for VRefMut<'a, T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
sanity_checks::<T>();
|
||||||
|
unsafe { T::map_to_mut(&mut self.inner) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: ?Sized + VTableMeta> VRefMut<'a, T> {
|
||||||
|
pub unsafe fn from_inner(inner: T::TraitObject) -> Self {
|
||||||
|
Self { inner, _phantom: core::marker::PhantomData }
|
||||||
|
}
|
||||||
|
pub unsafe fn inner(x: &Self) -> T::TraitObject {
|
||||||
|
x.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,23 +1,31 @@
|
||||||
use vtable::vtable;
|
use vtable::*;
|
||||||
#[vtable]
|
#[vtable]
|
||||||
/// This is the actual doc
|
/// This is the actual doc
|
||||||
struct HelloVTable {
|
struct HelloVTable {
|
||||||
foo: fn(*const HelloVTable, *mut HelloImpl, u32) -> u32,
|
foo: fn(VRef<'_, HelloVTable>, u32) -> u32,
|
||||||
|
foo_mut: fn(VRefMut<'_, HelloVTable>, u32) -> u32,
|
||||||
construct: fn(*const HelloVTable, u32) -> Box<HelloImpl>,
|
construct: fn(*const HelloVTable, u32) -> VBox<HelloVTable>,
|
||||||
|
|
||||||
assoc: fn(*const HelloVTable) -> isize,
|
assoc: fn(*const HelloVTable) -> isize,
|
||||||
|
|
||||||
drop: fn(*const HelloVTable, *mut HelloImpl),
|
drop: fn(VRefMut<'_, HelloVTable>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct SomeStruct(u32);
|
struct SomeStruct(u32);
|
||||||
impl Hello for SomeStruct {
|
impl Hello for SomeStruct {
|
||||||
fn foo(&mut self, xx: u32) -> u32 {
|
fn foo(&self, xx: u32) -> u32 {
|
||||||
|
println!("calling foo {} + {}", self.0, xx);
|
||||||
self.0 + xx
|
self.0 + xx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn foo_mut(&mut self, xx: u32) -> u32 {
|
||||||
|
self.0 += xx;
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fn construct(init: u32) -> Self {
|
fn construct(init: u32) -> Self {
|
||||||
|
println!("calling Construct {}", init);
|
||||||
Self(init)
|
Self(init)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,8 +36,14 @@ impl Hello for SomeStruct {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test() {
|
fn test() {
|
||||||
let vt = HelloVTable::new::<SomeStruct>();
|
//let vt = HelloVTable::new::<SomeStruct>();
|
||||||
|
let mut vt = HelloVTable::new::<SomeStruct>();
|
||||||
|
let vt = unsafe { HelloType::from_raw(std::ptr::NonNull::from(&mut vt)) };
|
||||||
assert_eq!(vt.assoc(), 32);
|
assert_eq!(vt.assoc(), 32);
|
||||||
let mut bx = vt.construct(89);
|
let mut bx = vt.construct(89);
|
||||||
assert_eq!(bx.foo(1), 90);
|
assert_eq!(bx.foo(1), 90);
|
||||||
|
assert_eq!(bx.foo_mut(1), 91);
|
||||||
|
assert_eq!(bx.foo(1), 92);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ fn main() {
|
||||||
cbindgen::Builder::new()
|
cbindgen::Builder::new()
|
||||||
.with_config(config)
|
.with_config(config)
|
||||||
.with_crate(crate_dir)
|
.with_crate(crate_dir)
|
||||||
|
.with_header("#include <vtable.h>")
|
||||||
.generate()
|
.generate()
|
||||||
.expect("Unable to generate bindings")
|
.expect("Unable to generate bindings")
|
||||||
.write_to_file(env::var("OUT_DIR").unwrap() + "/sixtyfps_internal.h");
|
.write_to_file(env::var("OUT_DIR").unwrap() + "/sixtyfps_internal.h");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue