use super::*;
use quote::{quote, quote_spanned};
use syn::{parse_quote, parse_quote_spanned};
pub(crate) fn gen_all(inputs: &ImplementInputs) -> Vec<syn::Item> {
let mut items: Vec<syn::Item> = Vec::with_capacity(64);
items.push(gen_original_impl(inputs));
items.push(gen_impl_struct(inputs));
items.push(gen_impl_deref(inputs));
items.push(gen_impl_impl(inputs));
items.push(gen_iunknown_impl(inputs));
items.push(gen_impl_com_object_inner(inputs));
items.extend(gen_impl_from(inputs));
items.extend(gen_impl_com_object_interfaces(inputs));
for (i, interface_chain) in inputs.interface_chains.iter().enumerate() {
items.push(gen_impl_as_impl(inputs, interface_chain, i));
}
items
}
fn gen_original_impl(inputs: &ImplementInputs) -> syn::Item {
let original_ident = &inputs.original_ident;
let generics = &inputs.generics;
let constraints = &inputs.constraints;
let mut output: syn::ItemImpl = parse_quote! {
impl #generics #original_ident::#generics where #constraints {}
};
output.items.push(gen_into_outer(inputs));
if !inputs.is_generic {
output.items.push(gen_into_static(inputs));
}
syn::Item::Impl(output)
}
fn gen_impl_struct(inputs: &ImplementInputs) -> syn::Item {
let impl_ident = &inputs.impl_ident;
let generics = &inputs.generics;
let constraints = &inputs.constraints;
let original_ident = &inputs.original_ident;
let vis = &inputs.original_type.vis;
let mut impl_fields = quote! {
identity: &'static ::windows_core::IInspectable_Vtbl,
};
for interface_chain in inputs.interface_chains.iter() {
let vtbl_ty = interface_chain.implement.to_vtbl_ident();
let chain_field_ident = &interface_chain.field_ident;
impl_fields.extend(quote! {
#chain_field_ident: &'static #vtbl_ty,
});
}
impl_fields.extend(quote! {
this: #original_ident::#generics,
count: ::windows_core::imp::WeakRefCount,
});
parse_quote! {
#[repr(C)]
#[allow(non_camel_case_types)]
#vis struct #impl_ident #generics where #constraints {
#impl_fields
}
}
}
fn gen_impl_deref(inputs: &ImplementInputs) -> syn::Item {
let generics = &inputs.generics;
let constraints = &inputs.constraints;
let original_ident = &inputs.original_type.ident;
let impl_ident = &inputs.impl_ident;
parse_quote! {
impl #generics ::core::ops::Deref for #impl_ident::#generics where #constraints {
type Target = #original_ident::#generics;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.this
}
}
}
}
fn gen_impl_impl(inputs: &ImplementInputs) -> syn::Item {
let impl_ident = &inputs.impl_ident;
let generics = &inputs.generics;
let constraints = &inputs.constraints;
let mut output: syn::ItemImpl = parse_quote! {
impl #generics #impl_ident::#generics where #constraints {}
};
let identity_type = if let Some(first) = inputs.interface_chains.first() {
first.implement.to_ident()
} else {
quote! { ::windows_core::IInspectable }
};
output.items.push(parse_quote! {
const VTABLE_IDENTITY: ::windows_core::IInspectable_Vtbl =
::windows_core::IInspectable_Vtbl::new::<
#impl_ident::#generics,
#identity_type,
0,
>();
});
for (interface_index, interface_chain) in inputs.interface_chains.iter().enumerate() {
let vtbl_ty = interface_chain.implement.to_vtbl_ident();
let vtable_const_ident = &interface_chain.vtable_const_ident;
let chain_offset_in_pointers: isize = -1 - interface_index as isize;
output.items.push(parse_quote! {
const #vtable_const_ident: #vtbl_ty = #vtbl_ty::new::<
#impl_ident::#generics,
#chain_offset_in_pointers,
>();
});
}
syn::Item::Impl(output)
}
fn gen_iunknown_impl(inputs: &ImplementInputs) -> syn::Item {
let generics = &inputs.generics;
let constraints = &inputs.constraints;
let impl_ident = &inputs.impl_ident;
let original_ident = &inputs.original_type.ident;
let trust_level = proc_macro2::Literal::usize_unsuffixed(inputs.trust_level);
let mut output: syn::ItemImpl = parse_quote! {
impl #generics ::windows_core::IUnknownImpl for #impl_ident::#generics where #constraints {
type Impl = #original_ident::#generics;
#[inline(always)]
fn get_impl(&self) -> &Self::Impl {
&self.this
}
#[inline(always)]
fn get_impl_mut(&mut self) -> &mut Self::Impl {
&mut self.this
}
#[inline(always)]
fn into_inner(self) -> Self::Impl {
self.this
}
#[inline(always)]
fn AddRef(&self) -> u32 {
self.count.add_ref()
}
#[inline(always)]
unsafe fn Release(self_: *mut Self) -> u32 {
let remaining = (*self_).count.release();
if remaining == 0 {
_ = ::windows_core::imp::Box::from_raw(self_);
}
remaining
}
#[inline(always)]
fn is_reference_count_one(&self) -> bool {
self.count.is_one()
}
unsafe fn GetTrustLevel(&self, value: *mut i32) -> ::windows_core::HRESULT {
if value.is_null() {
return ::windows_core::imp::E_POINTER;
}
*value = #trust_level;
::windows_core::HRESULT(0)
}
fn to_object(&self) -> ::windows_core::ComObject<Self::Impl> {
self.count.add_ref();
unsafe {
::windows_core::ComObject::from_raw(
::core::ptr::NonNull::new_unchecked(self as *const Self as *mut Self)
)
}
}
}
};
let query_interface_fn = gen_query_interface(inputs);
output.items.push(syn::ImplItem::Fn(query_interface_fn));
syn::Item::Impl(output)
}
fn gen_impl_com_object_inner(inputs: &ImplementInputs) -> syn::Item {
let original_ident = &inputs.original_type.ident;
let generics = &inputs.generics;
let constraints = &inputs.constraints;
let impl_ident = &inputs.impl_ident;
parse_quote! {
impl #generics ::windows_core::ComObjectInner for #original_ident::#generics where #constraints {
type Outer = #impl_ident::#generics;
fn into_object(self) -> ::windows_core::ComObject<Self> {
let boxed = ::windows_core::imp::Box::<#impl_ident::#generics>::new(self.into_outer());
unsafe {
let ptr = ::windows_core::imp::Box::into_raw(boxed);
::windows_core::ComObject::from_raw(
::core::ptr::NonNull::new_unchecked(ptr)
)
}
}
}
}
}
fn gen_query_interface(inputs: &ImplementInputs) -> syn::ImplItemFn {
let queries = inputs.interface_chains.iter().map(|interface_chain| {
let chain_ty = interface_chain.implement.to_vtbl_ident();
let chain_field = &interface_chain.field_ident;
quote_spanned! {
interface_chain.implement.span =>
if #chain_ty::matches(&iid) {
break 'found &self.#chain_field as *const _ as *const ::core::ffi::c_void;
}
}
});
let enable_dyn_casting = inputs.original_type.generics.lifetimes().count() == 0;
let dynamic_cast_query = if enable_dyn_casting {
quote! {
if iid == ::windows_core::DYNAMIC_CAST_IID {
(interface as *mut *const dyn core::any::Any).write(self as &dyn ::core::any::Any as *const dyn ::core::any::Any);
return ::windows_core::HRESULT(0);
}
}
} else {
quote!()
};
let identity_query = if inputs.agile {
quote! {
if iid == <::windows_core::IUnknown as ::windows_core::Interface>::IID
|| iid == <::windows_core::IInspectable as ::windows_core::Interface>::IID
|| iid == <::windows_core::imp::IAgileObject as ::windows_core::Interface>::IID {
break 'found &self.identity as *const _ as *const ::core::ffi::c_void;
}
}
} else {
quote! {
if iid == <::windows_core::IUnknown as ::windows_core::Interface>::IID
|| iid == <::windows_core::IInspectable as ::windows_core::Interface>::IID {
break 'found &self.identity as *const _ as *const ::core::ffi::c_void;
}
}
};
let marshal_query = if inputs.agile {
quote! {
#[cfg(windows)]
if iid == <::windows_core::imp::IMarshal as ::windows_core::Interface>::IID {
return ::windows_core::imp::marshaler(self.to_interface(), interface);
}
}
} else {
quote! {}
};
let tear_off_query = quote! {
let tear_off_ptr = self.count.query(&iid, &self.identity as *const _ as *mut _);
if !tear_off_ptr.is_null() {
*interface = tear_off_ptr;
return ::windows_core::HRESULT(0);
}
};
parse_quote! {
unsafe fn QueryInterface(
&self,
iid: *const ::windows_core::GUID,
interface: *mut *mut ::core::ffi::c_void,
) -> ::windows_core::HRESULT {
unsafe {
if iid.is_null() || interface.is_null() {
return ::windows_core::imp::E_POINTER;
}
let iid = *iid;
let interface_ptr: *const ::core::ffi::c_void = 'found: {
#identity_query
#(#queries)*
#marshal_query
#dynamic_cast_query
#tear_off_query
*interface = ::core::ptr::null_mut();
return ::windows_core::imp::E_NOINTERFACE;
};
debug_assert!(!interface_ptr.is_null());
*interface = interface_ptr as *mut ::core::ffi::c_void;
self.count.add_ref();
return ::windows_core::HRESULT(0);
}
}
}
}
fn gen_into_outer(inputs: &ImplementInputs) -> syn::ImplItem {
let generics = &inputs.generics;
let impl_ident = &inputs.impl_ident;
let mut initializers = quote! {
identity: &#impl_ident::#generics::VTABLE_IDENTITY,
};
for interface_chain in inputs.interface_chains.iter() {
let vtbl_field_ident = &interface_chain.field_ident;
let vtable_const_ident = &interface_chain.vtable_const_ident;
initializers.extend(quote_spanned! {
interface_chain.implement.span =>
#vtbl_field_ident: &#impl_ident::#generics::#vtable_const_ident,
});
}
let maybe_const = if inputs.is_generic {
quote!()
} else {
quote!(const)
};
parse_quote! {
#[inline(always)]
#maybe_const fn into_outer(self) -> #impl_ident::#generics {
#impl_ident::#generics {
#initializers
count: ::windows_core::imp::WeakRefCount::new(),
this: self,
}
}
}
}
fn gen_into_static(inputs: &ImplementInputs) -> syn::ImplItem {
assert!(!inputs.is_generic);
parse_quote! {
pub const fn into_static(self) -> ::windows_core::StaticComObject<Self> {
::windows_core::StaticComObject::from_outer(self.into_outer())
}
}
}
fn gen_impl_from(inputs: &ImplementInputs) -> Vec<syn::Item> {
let mut items = Vec::new();
let original_ident = &inputs.original_type.ident;
let generics = &inputs.generics;
let constraints = &inputs.constraints;
items.push(parse_quote! {
impl #generics ::core::convert::From<#original_ident::#generics> for ::windows_core::IUnknown where #constraints {
#[inline(always)]
fn from(this: #original_ident::#generics) -> Self {
let com_object = ::windows_core::ComObject::new(this);
com_object.into_interface()
}
}
});
items.push(parse_quote! {
impl #generics ::core::convert::From<#original_ident::#generics> for ::windows_core::IInspectable where #constraints {
#[inline(always)]
fn from(this: #original_ident::#generics) -> Self {
let com_object = ::windows_core::ComObject::new(this);
com_object.into_interface()
}
}
});
for interface_chain in inputs.interface_chains.iter() {
let interface_ident = interface_chain.implement.to_ident();
items.push(parse_quote_spanned! {
interface_chain.implement.span =>
impl #generics ::core::convert::From<#original_ident::#generics> for #interface_ident where #constraints {
#[inline(always)]
fn from(this: #original_ident::#generics) -> Self {
let com_object = ::windows_core::ComObject::new(this);
com_object.into_interface()
}
}
});
}
items
}
fn gen_impl_com_object_interfaces(inputs: &ImplementInputs) -> Vec<syn::Item> {
let mut items = Vec::new();
let generics = &inputs.generics;
let constraints = &inputs.constraints;
let impl_ident = &inputs.impl_ident;
items.push(parse_quote! {
impl #generics ::windows_core::ComObjectInterface<::windows_core::IUnknown> for #impl_ident::#generics where #constraints {
#[inline(always)]
fn as_interface_ref(&self) -> ::windows_core::InterfaceRef<'_, ::windows_core::IUnknown> {
unsafe {
let interface_ptr = &self.identity;
::core::mem::transmute(interface_ptr)
}
}
}
});
items.push(parse_quote! {
impl #generics ::windows_core::ComObjectInterface<::windows_core::IInspectable> for #impl_ident::#generics where #constraints {
#[inline(always)]
fn as_interface_ref(&self) -> ::windows_core::InterfaceRef<'_, ::windows_core::IInspectable> {
unsafe {
let interface_ptr = &self.identity;
::core::mem::transmute(interface_ptr)
}
}
}
});
for interface_chain in inputs.interface_chains.iter() {
let chain_field = &interface_chain.field_ident;
let interface_ident = interface_chain.implement.to_ident();
items.push(parse_quote_spanned! {
interface_chain.implement.span =>
#[allow(clippy::needless_lifetimes)]
impl #generics ::windows_core::ComObjectInterface<#interface_ident> for #impl_ident::#generics where #constraints {
#[inline(always)]
fn as_interface_ref(&self) -> ::windows_core::InterfaceRef<'_, #interface_ident> {
unsafe {
::core::mem::transmute(&self.#chain_field)
}
}
}
});
}
items
}
fn gen_impl_as_impl(
inputs: &ImplementInputs,
interface_chain: &InterfaceChain,
interface_chain_index: usize,
) -> syn::Item {
let generics = &inputs.generics;
let constraints = &inputs.constraints;
let interface_ident = interface_chain.implement.to_ident();
let original_ident = &inputs.original_type.ident;
let impl_ident = &inputs.impl_ident;
parse_quote_spanned! {
interface_chain.implement.span =>
impl #generics ::windows_core::AsImpl<#original_ident::#generics> for #interface_ident where #constraints {
#[inline(always)]
unsafe fn as_impl_ptr(&self) -> ::core::ptr::NonNull<#original_ident::#generics> {
unsafe {
let this = ::windows_core::Interface::as_raw(self);
let this = (this as *mut *mut ::core::ffi::c_void).sub(1 + #interface_chain_index) as *mut #impl_ident::#generics;
::core::ptr::NonNull::new_unchecked(::core::ptr::addr_of!((*this).this) as *const #original_ident::#generics as *mut #original_ident::#generics)
}
}
}
}
}