Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for non-COM interfaces #2066

Merged
merged 4 commits into from
Sep 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/libs/bindgen/src/classes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ fn gen_class(gen: &Gen, def: TypeDef) -> TokenStream {

tokens.combine(&gen.interface_core_traits(def, &[], &name, &TokenStream::new(), &TokenStream::new(), &features));
tokens.combine(&gen.interface_winrt_trait(def, &[], &name, &TokenStream::new(), &TokenStream::new(), &features));
tokens.combine(&gen.interface_trait(def, &[], &name, &TokenStream::new(), &features));
tokens.combine(&gen.interface_trait(def, &[], &name, &TokenStream::new(), &features, true));
tokens.combine(&gen.runtime_name_trait(def, &[], &name, &TokenStream::new(), &features));
tokens.combine(&gen.async_get(def, &[], &name, &TokenStream::new(), &TokenStream::new(), &features));
tokens.combine(&iterators::gen(gen, def, &[], &name, &TokenStream::new(), &TokenStream::new(), &cfg));
Expand Down
2 changes: 1 addition & 1 deletion crates/libs/bindgen/src/delegates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ fn gen_win_delegate(gen: &Gen, def: TypeDef) -> TokenStream {
};

tokens.combine(&gen.interface_core_traits(def, generics, &ident, &constraints, &phantoms, &features));
tokens.combine(&gen.interface_trait(def, generics, &ident, &constraints, &features));
tokens.combine(&gen.interface_trait(def, generics, &ident, &constraints, &features, true));
tokens.combine(&gen.interface_winrt_trait(def, generics, &ident, &constraints, &phantoms, &features));
tokens.combine(&gen.interface_vtbl(def, generics, &ident, &constraints, &features));
tokens
Expand Down
20 changes: 14 additions & 6 deletions crates/libs/bindgen/src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ impl<'a> Gen<'a> {
}
}

pub fn interface_trait(&self, def: TypeDef, generics: &[Type], ident: &TokenStream, constraints: &TokenStream, features: &TokenStream) -> TokenStream {
pub fn interface_trait(&self, def: TypeDef, generics: &[Type], ident: &TokenStream, constraints: &TokenStream, features: &TokenStream, has_unknown_base: bool) -> TokenStream {
if let Some(default) = self.reader.type_def_default_interface(def) {
let default_name = self.type_name(&default);
let vtbl = self.type_vtbl_name(&default);
Expand Down Expand Up @@ -737,16 +737,24 @@ impl<'a> Gen<'a> {
::windows::core::GUID::from_signature(<Self as ::windows::core::RuntimeType>::SIGNATURE)
}
};
quote! {

let mut tokens = quote! {
#features
unsafe impl<#constraints> ::windows::core::Vtable for #ident {
type Vtable = #vtbl;
}
#features
unsafe impl<#constraints> ::windows::core::Interface for #ident {
const IID: ::windows::core::GUID = #guid;
}
};

if has_unknown_base {
tokens.combine(&quote! {
#features
unsafe impl<#constraints> ::windows::core::Interface for #ident {
const IID: ::windows::core::GUID = #guid;
}
});
}

tokens
}
}
pub fn interface_vtbl(&self, def: TypeDef, generics: &[Type], _ident: &TokenStream, constraints: &TokenStream, features: &TokenStream) -> TokenStream {
Expand Down
100 changes: 77 additions & 23 deletions crates/libs/bindgen/src/implements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub fn gen(gen: &Gen, def: TypeDef) -> TokenStream {
let type_ident = to_ident(gen.reader.type_def_name(def));
let impl_ident = type_ident.join("_Impl");
let vtbl_ident = type_ident.join("_Vtbl");
let implvtbl_ident = impl_ident.join("Vtbl");
let constraints = gen.generic_constraints(generics);
let generic_names = gen.generic_names(generics);
let named_phantoms = gen.generic_named_phantoms(generics);
Expand All @@ -17,6 +18,7 @@ pub fn gen(gen: &Gen, def: TypeDef) -> TokenStream {
let mut requires = quote! {};
let type_ident = quote! { #type_ident<#generic_names> };
let vtables = gen.reader.type_def_vtables(def);
let has_unknown_base = matches!(vtables.first(), Some(Type::IUnknown));

fn gen_required_trait(gen: &Gen, def: TypeDef, generics: &[Type]) -> TokenStream {
let name = gen.type_def_name_imp(def, generics, "_Impl");
Expand Down Expand Up @@ -83,24 +85,38 @@ pub fn gen(gen: &Gen, def: TypeDef) -> TokenStream {

let invoke_upcall = if gen.reader.type_def_flags(def).winrt() { winrt_methods::gen_upcall(gen, &signature, quote! { this.#name }) } else { com_methods::gen_upcall(gen, &signature, quote! { this.#name }) };

quote! {
unsafe extern "system" fn #name<#constraints Identity: ::windows::core::IUnknownImpl<Impl = Impl>, Impl: #impl_ident<#generic_names>, const OFFSET: isize> #vtbl_signature {
// offset the `this` pointer by `OFFSET` times the size of a pointer and cast it as an IUnknown implementation
let this = (this as *const *const ()).offset(OFFSET) as *const Identity;
let this = (*this).get_impl();
#invoke_upcall
if has_unknown_base {
quote! {
unsafe extern "system" fn #name<#constraints Identity: ::windows::core::IUnknownImpl<Impl = Impl>, Impl: #impl_ident<#generic_names>, const OFFSET: isize> #vtbl_signature {
// offset the `this` pointer by `OFFSET` times the size of a pointer and cast it as an IUnknown implementation
let this = (this as *const *const ()).offset(OFFSET) as *const Identity;
let this = (*this).get_impl();
#invoke_upcall
}
}
} else {
quote! {
unsafe extern "system" fn #name<Impl: #impl_ident> #vtbl_signature {
let this = (this as *mut *mut ::core::ffi::c_void) as *const ::windows::core::ScopedHeap;
let this = &*((*this).this as *const Impl);
#invoke_upcall
}
}
}
});

let mut methods = quote! {};

match gen.reader.type_def_vtables(def).last() {
match vtables.last() {
Some(Type::IUnknown) => methods.combine(&quote! { base__: ::windows::core::IUnknown_Vtbl::new::<Identity, OFFSET>(), }),
Some(Type::IInspectable) => methods.combine(&quote! { base__: ::windows::core::IInspectable_Vtbl::new::<Identity, #type_ident, OFFSET>(), }),
Some(Type::TypeDef((def, generics))) => {
let name = gen.type_def_name_imp(*def, generics, "_Vtbl");
methods.combine(&quote! { base__: #name::new::<Identity, Impl, OFFSET>(), });
if has_unknown_base {
methods.combine(&quote! { base__: #name::new::<Identity, Impl, OFFSET>(), });
} else {
methods.combine(&quote! { base__: #name::new::<Impl>(), });
}
}
_ => {}
}
Expand All @@ -110,26 +126,64 @@ pub fn gen(gen: &Gen, def: TypeDef) -> TokenStream {

for method in gen.reader.type_def_methods(def) {
let name = method_names.add(gen, method);
methods.combine(&quote! { #name: #name::<#generic_names Identity, Impl, OFFSET>, });
if has_unknown_base {
methods.combine(&quote! { #name: #name::<#generic_names Identity, Impl, OFFSET>, });
} else {
methods.combine(&quote! { #name: #name::<Impl>, });
}
}

quote! {
#features
pub trait #impl_ident<#generic_names> : Sized #requires where #constraints {
#(#method_traits)*
if has_unknown_base {
quote! {
#features
pub trait #impl_ident<#generic_names> : Sized #requires where #constraints {
#(#method_traits)*
}
#runtime_name
#features
impl<#constraints> #vtbl_ident<#generic_names> {
pub const fn new<Identity: ::windows::core::IUnknownImpl<Impl = Impl>, Impl: #impl_ident<#generic_names>, const OFFSET: isize>() -> #vtbl_ident<#generic_names> {
#(#method_impls)*
Self{
#methods
#(#named_phantoms)*
}
}
pub fn matches(iid: &windows::core::GUID) -> bool {
#matches
}
}
}
#runtime_name
#features
impl<#constraints> #vtbl_ident<#generic_names> {
pub const fn new<Identity: ::windows::core::IUnknownImpl<Impl = Impl>, Impl: #impl_ident<#generic_names>, const OFFSET: isize>() -> #vtbl_ident<#generic_names> {
#(#method_impls)*
Self{
#methods
#(#named_phantoms)*
} else {
quote! {
#features
pub trait #impl_ident : Sized #requires {
#(#method_traits)*
}
#features
impl #vtbl_ident {
pub const fn new<Impl: #impl_ident>() -> #vtbl_ident {
#(#method_impls)*
Self{
#methods
#(#named_phantoms)*
}
}
}
pub fn matches(iid: &windows::core::GUID) -> bool {
#matches
#[doc(hidden)]
#features
struct #implvtbl_ident<T: #impl_ident> (::std::marker::PhantomData<T>);
#features
impl<T: #impl_ident> #implvtbl_ident<T> {
const VTABLE: #vtbl_ident = #vtbl_ident::new::<T>();
}
#features
impl #type_ident {
pub fn new<'a, T: #impl_ident>(this: &'a T) -> ::windows::core::ScopedInterface<'a, Self> {
let this = ::windows::core::ScopedHeap { vtable: &#implvtbl_ident::<T>::VTABLE as *const _ as *const _, this: this as *const _ as *const _ };
let this = ::std::mem::ManuallyDrop::new(::std::boxed::Box::new(this));
unsafe { ::windows::core::ScopedInterface::new(::std::mem::transmute(&this.vtable)) }
}
}
}
}
Expand Down
Loading