Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

600
vendor/windows-implement/src/gen.rs vendored Normal file
View File

@@ -0,0 +1,600 @@
//! Generates output for the `implement` proc macro.
//!
//! Each function in this module focuses on generating one thing, or one kind of thing.
//! Each takes `ImplementInputs` as its input. `gen_all` calls all of the `gen_*` functions
//! and merges them into the final list of output items.
//!
//! We use `parse_quote` so that we can verify that a given function generates a well-formed AST
//! item, within the narrowest possible scope. This allows us to detect errors more quickly during
//! development. If the input to `parse_quote` cannot be parsed, then the macro will panic and
//! the panic will point to the specific `parse_quote` call, rather than the entire output of the
//! `implement` proc macro being unparsable. This greatly aids in development.
use super::*;
use quote::{quote, quote_spanned};
use syn::{parse_quote, parse_quote_spanned};
/// Generates code for the `#[implements]` macro.
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
}
/// Generates an `impl` block for the original `Foo` type.
///
/// This `impl` block will contain `into_outer` and `into_static` (if applicable).
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));
// Static COM objects have a lot of constraints. They can't be generic (open parameters),
// because that would be meaningless (an open generic type cannot have a known representation).
//
// Right now, we can't generate static COM objects that have base classes because we rely on
// boxing and then unboxing during construction of aggregated types.
if !inputs.is_generic {
output.items.push(gen_into_static(inputs));
}
syn::Item::Impl(output)
}
/// Generates the structure definition for the `Foo_Impl` type.
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
}
}
}
/// Generates the implementation of `core::ops::Deref` for the generated `Foo_Impl` type.
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
}
}
}
}
/// Generates an `impl` block for the generated `Foo_Impl` block.
///
/// This generates:
///
/// ```rust,ignore
/// const VTABLE_IDENTITY = IInspectable_Vtbl = ...;
/// const VTABLE_INTERFACE1_IFOO: IFoo_Vtbl = ...;
/// const VTABLE_INTERFACE2_IBAR: IBar_Vtbl = ...;
/// ```
///
/// These constants are used when constructing vtables. The benefit of using constants instead
/// of directly generating these expressions is that it allows us to overcome limitations in
/// using generics in constant contexts. Right now, Rust has a lot of limitations around using
/// constants in constant contexts. Fortunately, associated constants (constants defined within
/// `impl` blocks) work in stable Rust, even for generic types.
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 {}
};
// This is here so that IInspectable::GetRuntimeClassName can work properly.
// For a test case for this, see crates/tests/misc/component_client.
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)
}
/// Generates the `IUnknownImpl` implementation for the `Foo_Impl` type.
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)
}
/// Generates the implementation of `ComObjectInner`.
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;
// IMPORTANT! This function handles assembling the "boxed" type of a COM object.
// It immediately moves the box into a heap allocation (box) and returns only a ComObject
// reference that points to it. We intentionally _do not_ expose any owned instances of
// Foo_Impl to safe Rust code, because doing so would allow unsound behavior in safe Rust
// code, due to the adjustments of the reference count that Foo_Impl permits.
//
// This is why this function returns ComObject<Self> instead of returning #impl_ident.
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)
)
}
}
}
}
}
/// Generates the `query_interface` method.
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;
}
}
});
// Dynamic casting requires that the object not contain non-static lifetimes.
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 {
// DYNAMIC_CAST_IID is special. We _do not_ increase the reference count for this pseudo-interface.
// Also, instead of returning an interface pointer, we simply write the `&dyn Any` directly to the
// 'interface' pointer. Since the size of `&dyn Any` is 2 pointers, not one, the caller must be
// prepared for this. This is not a normal QueryInterface call.
//
// See the `Interface::cast_to_any` method, which is the only caller that should use DYNAMIC_CAST_ID.
(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);
}
}
}
}
/// Generates the `T::into_outer` function. This function is part of how we construct a
/// `ComObject<T>` from a `T`.
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,
});
}
// If the type is generic then into_outer() cannot be a const fn.
let maybe_const = if inputs.is_generic {
quote!()
} else {
quote!(const)
};
parse_quote! {
// This constructs an "outer" object. This should only be used by the implementation
// of the outer object, never by application code.
//
// The callers of this function (`into_static` and `into_object`) are both responsible
// for maintaining one of our invariants: Application code never has an owned instance
// of the outer (implementation) type. into_static() maintains this invariant by
// returning a wrapped StaticComObject value, which owns its contents but never gives
// application code a way to mutably access its contents. This prevents the refcount
// shearing problem.
//
// TODO: Make it impossible for app code to call this function, by placing it in a
// module and marking this as private to the module.
#[inline(always)]
#maybe_const fn into_outer(self) -> #impl_ident::#generics {
#impl_ident::#generics {
#initializers
count: ::windows_core::imp::WeakRefCount::new(),
this: self,
}
}
}
}
/// Generates the `T::into_static` function. This function is part of how we construct a
/// `StaticComObject<T>` from a `T`.
fn gen_into_static(inputs: &ImplementInputs) -> syn::ImplItem {
assert!(!inputs.is_generic);
parse_quote! {
/// This converts a partially-constructed COM object (in the sense that it contains
/// application state but does not yet have vtable and reference count constructed)
/// into a `StaticComObject`. This allows the COM object to be stored in static
/// (global) variables.
pub const fn into_static(self) -> ::windows_core::StaticComObject<Self> {
::windows_core::StaticComObject::from_outer(self.into_outer())
}
}
}
/// Generates `From`-based conversions.
///
/// These conversions convert from the user's type `T` to `ComObject<T>` or to an interface
/// implemented by `T`. These conversions are shorthand for calling `ComObject::new(value)`.
///
/// We can only generate conversions from `T` to the roots of each interface chain. We can't
/// generate `From` conversions from `T` to an interface that is inherited by an interface chain,
/// because this proc macro does not have access to any information about the inheritance chain
/// of interfaces that are referenced.
///
/// For example:
///
/// ```rust,ignore
/// #[implement(IFoo3)]
/// struct MyType;
/// ```
///
/// If `IFoo3` inherits from `IFoo2`, then this code will _not_ generate a conversion for `IFoo2`.
/// However, user code can still do this:
///
/// ```rust,ignore
/// let ifoo2 = IFoo3::from(MyType).into();
/// ```
///
/// This works because the `IFoo3` type has an `Into` impl for `IFoo2`.
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
}
/// Generates the `ComObjectInterface` implementation for each interface chain.
///
/// Each of these `impl` blocks says "this COM object implements this COM interface".
/// It allows the `ComObject` type to do conversions from the `ComObject` to `IFoo` instances,
/// _without_ doing a `QueryInterface` call.
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
}
/// Generates the implementation of the `AsImpl` trait for a given interface chain.
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 {
// SAFETY: the offset is guaranteed to be in bounds, and the implementation struct
// is guaranteed to live at least as long as `self`.
#[inline(always)]
unsafe fn as_impl_ptr(&self) -> ::core::ptr::NonNull<#original_ident::#generics> {
unsafe {
let this = ::windows_core::Interface::as_raw(self);
// Subtract away the vtable offset plus 1, for the `identity` field, to get
// to the impl struct which contains that original implementation type.
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)
}
}
}
}
}

406
vendor/windows-implement/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,406 @@
//! Implement COM interfaces for Rust types.
//!
//! Take a look at [macro@implement] for an example.
//!
//! Learn more about Rust for Windows here: <https://github.com/microsoft/windows-rs>
use quote::{quote, ToTokens};
mod r#gen;
use r#gen::gen_all;
#[cfg(test)]
mod tests;
/// Implements one or more COM interfaces.
///
/// # Example
/// ```rust,no_run
/// use windows_core::*;
///
/// #[interface("094d70d6-5202-44b8-abb8-43860da5aca2")]
/// unsafe trait IValue: IUnknown {
/// fn GetValue(&self, value: *mut i32) -> HRESULT;
/// }
///
/// #[implement(IValue)]
/// struct Value(i32);
///
/// impl IValue_Impl for Value_Impl {
/// unsafe fn GetValue(&self, value: *mut i32) -> HRESULT {
/// *value = self.0;
/// HRESULT(0)
/// }
/// }
///
/// let object: IValue = Value(123).into();
/// // Call interface methods...
/// ```
#[proc_macro_attribute]
pub fn implement(
attributes: proc_macro::TokenStream,
type_tokens: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
implement_core(attributes.into(), type_tokens.into()).into()
}
fn implement_core(
attributes: proc_macro2::TokenStream,
item_tokens: proc_macro2::TokenStream,
) -> proc_macro2::TokenStream {
let attributes = syn::parse2::<ImplementAttributes>(attributes).unwrap();
let original_type = syn::parse2::<syn::ItemStruct>(item_tokens).unwrap();
// Do a little thinking and assemble ImplementInputs. We pass ImplementInputs to
// all of our gen_* function.
let inputs = ImplementInputs {
original_ident: original_type.ident.clone(),
interface_chains: convert_implements_to_interface_chains(attributes.implement),
trust_level: attributes.trust_level,
agile: attributes.agile,
impl_ident: quote::format_ident!("{}_Impl", &original_type.ident),
constraints: {
if let Some(where_clause) = &original_type.generics.where_clause {
where_clause.predicates.to_token_stream()
} else {
quote!()
}
},
generics: if !original_type.generics.params.is_empty() {
let mut params = quote! {};
original_type.generics.params.to_tokens(&mut params);
quote! { <#params> }
} else {
quote! { <> }
},
is_generic: !original_type.generics.params.is_empty(),
original_type,
};
let items = gen_all(&inputs);
let mut tokens = inputs.original_type.into_token_stream();
for item in items {
tokens.extend(item.into_token_stream());
}
tokens
}
/// This provides the inputs to the `gen_*` functions, which generate the proc macro output.
struct ImplementInputs {
/// The user's type that was marked with `#[implement]`.
original_type: syn::ItemStruct,
/// The identifier for the user's original type definition.
original_ident: syn::Ident,
/// The list of interface chains that this type implements.
interface_chains: Vec<InterfaceChain>,
/// The "trust level", which is returned by `IInspectable::GetTrustLevel`.
trust_level: usize,
/// Determines whether `IAgileObject` and `IMarshal` are implemented automatically.
agile: bool,
/// The identifier of the `Foo_Impl` type.
impl_ident: syn::Ident,
/// The list of constraints needed for this `Foo_Impl` type.
constraints: proc_macro2::TokenStream,
/// The list of generic parameters for this `Foo_Impl` type, including `<` and `>`.
/// If there are no generics, this contains `<>`.
generics: proc_macro2::TokenStream,
/// True if the user type has any generic parameters.
is_generic: bool,
}
/// Describes one COM interface chain.
struct InterfaceChain {
/// The name of the field for the vtable chain, e.g. `interface4_ifoo`.
field_ident: syn::Ident,
/// The name of the associated constant item for the vtable chain's initializer,
/// e.g. `INTERFACE4_IFOO_VTABLE`.
vtable_const_ident: syn::Ident,
implement: ImplementType,
}
struct ImplementType {
type_name: String,
generics: Vec<ImplementType>,
/// The best span for diagnostics.
span: proc_macro2::Span,
}
impl ImplementType {
fn to_ident(&self) -> proc_macro2::TokenStream {
let type_name = syn::parse_str::<proc_macro2::TokenStream>(&self.type_name)
.expect("Invalid token stream");
let generics = self.generics.iter().map(|g| g.to_ident());
quote! { #type_name<#(#generics,)*> }
}
fn to_vtbl_ident(&self) -> proc_macro2::TokenStream {
let ident = self.to_ident();
quote! {
<#ident as ::windows_core::Interface>::Vtable
}
}
}
#[derive(Default)]
struct ImplementAttributes {
pub implement: Vec<ImplementType>,
pub trust_level: usize,
pub agile: bool,
}
impl syn::parse::Parse for ImplementAttributes {
fn parse(cursor: syn::parse::ParseStream) -> syn::parse::Result<Self> {
let mut input = Self {
agile: true,
..Default::default()
};
while !cursor.is_empty() {
input.parse_implement(cursor)?;
}
Ok(input)
}
}
impl ImplementAttributes {
fn parse_implement(&mut self, cursor: syn::parse::ParseStream) -> syn::parse::Result<()> {
let tree = cursor.parse::<UseTree2>()?;
self.walk_implement(&tree, &mut String::new())?;
if !cursor.is_empty() {
cursor.parse::<syn::Token![,]>()?;
}
Ok(())
}
fn walk_implement(
&mut self,
tree: &UseTree2,
namespace: &mut String,
) -> syn::parse::Result<()> {
match tree {
UseTree2::Path(input) => {
if !namespace.is_empty() {
namespace.push_str("::");
}
namespace.push_str(&input.ident.to_string());
self.walk_implement(&input.tree, namespace)?;
}
UseTree2::Name(_) => {
self.implement.push(tree.to_element_type(namespace)?);
}
UseTree2::Group(input) => {
for tree in &input.items {
self.walk_implement(tree, namespace)?;
}
}
UseTree2::TrustLevel(input) => self.trust_level = *input,
UseTree2::Agile(agile) => self.agile = *agile,
}
Ok(())
}
}
enum UseTree2 {
Path(UsePath2),
Name(UseName2),
Group(UseGroup2),
TrustLevel(usize),
Agile(bool),
}
impl UseTree2 {
fn to_element_type(&self, namespace: &mut String) -> syn::parse::Result<ImplementType> {
match self {
Self::Path(input) => {
if !namespace.is_empty() {
namespace.push_str("::");
}
namespace.push_str(&input.ident.to_string());
input.tree.to_element_type(namespace)
}
Self::Name(input) => {
let mut type_name = input.ident.to_string();
let span = input.ident.span();
if !namespace.is_empty() {
type_name = format!("{namespace}::{type_name}");
}
let mut generics = vec![];
for g in &input.generics {
generics.push(g.to_element_type(&mut String::new())?);
}
Ok(ImplementType {
type_name,
generics,
span,
})
}
Self::Group(input) => Err(syn::parse::Error::new(
input.brace_token.span.join(),
"Syntax not supported",
)),
_ => unimplemented!(),
}
}
}
struct UsePath2 {
pub ident: syn::Ident,
pub tree: Box<UseTree2>,
}
struct UseName2 {
pub ident: syn::Ident,
pub generics: Vec<UseTree2>,
}
struct UseGroup2 {
pub brace_token: syn::token::Brace,
pub items: syn::punctuated::Punctuated<UseTree2, syn::Token![,]>,
}
impl syn::parse::Parse for UseTree2 {
fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(syn::Ident) {
use syn::ext::IdentExt;
let ident = input.call(syn::Ident::parse_any)?;
if input.peek(syn::Token![::]) {
input.parse::<syn::Token![::]>()?;
Ok(Self::Path(UsePath2 {
ident,
tree: Box::new(input.parse()?),
}))
} else if input.peek(syn::Token![=]) {
if ident == "TrustLevel" {
input.parse::<syn::Token![=]>()?;
let span = input.span();
let value = input.call(syn::Ident::parse_any)?;
match value.to_string().as_str() {
"Partial" => Ok(Self::TrustLevel(1)),
"Full" => Ok(Self::TrustLevel(2)),
_ => Err(syn::parse::Error::new(
span,
"`TrustLevel` must be `Partial` or `Full`",
)),
}
} else if ident == "Agile" {
input.parse::<syn::Token![=]>()?;
let span = input.span();
let value = input.call(syn::Ident::parse_any)?;
match value.to_string().as_str() {
"true" => Ok(Self::Agile(true)),
"false" => Ok(Self::Agile(false)),
_ => Err(syn::parse::Error::new(
span,
"`Agile` must be `true` or `false`",
)),
}
} else {
Err(syn::parse::Error::new(
ident.span(),
"Unrecognized key-value pair",
))
}
} else {
let generics = if input.peek(syn::Token![<]) {
input.parse::<syn::Token![<]>()?;
let mut generics = Vec::new();
loop {
generics.push(input.parse::<Self>()?);
if input.parse::<syn::Token![,]>().is_err() {
break;
}
}
input.parse::<syn::Token![>]>()?;
generics
} else {
Vec::new()
};
Ok(Self::Name(UseName2 { ident, generics }))
}
} else if lookahead.peek(syn::token::Brace) {
let content;
let brace_token = syn::braced!(content in input);
let items = content.parse_terminated(Self::parse, syn::Token![,])?;
Ok(Self::Group(UseGroup2 { brace_token, items }))
} else {
Err(lookahead.error())
}
}
}
fn convert_implements_to_interface_chains(implements: Vec<ImplementType>) -> Vec<InterfaceChain> {
let mut chains = Vec::with_capacity(implements.len());
for (i, implement) in implements.into_iter().enumerate() {
// Create an identifier for this interface chain.
// We only use this for naming fields; it is never visible to the developer.
// This helps with debugging.
//
// We use i + 1 so that it matches the numbering of our interface offsets. Interface 0
// is the "identity" interface.
let mut ident_string = format!("interface{}", i + 1);
let suffix = get_interface_ident_suffix(&implement.type_name);
if !suffix.is_empty() {
ident_string.push('_');
ident_string.push_str(&suffix);
}
let field_ident = syn::Ident::new(&ident_string, implement.span);
let mut vtable_const_string = ident_string.clone();
vtable_const_string.make_ascii_uppercase();
vtable_const_string.insert_str(0, "VTABLE_");
let vtable_const_ident = syn::Ident::new(&vtable_const_string, implement.span);
chains.push(InterfaceChain {
implement,
field_ident,
vtable_const_ident,
});
}
chains
}
fn get_interface_ident_suffix(type_name: &str) -> String {
let mut suffix = String::new();
for c in type_name.chars() {
let c = c.to_ascii_lowercase();
if suffix.len() >= 20 {
break;
}
if c.is_ascii_alphanumeric() {
suffix.push(c);
}
}
suffix
}

135
vendor/windows-implement/src/tests.rs vendored Normal file
View File

@@ -0,0 +1,135 @@
//! These tests are just a way to quickly run the `#[implement]` macro and see its output.
//! They don't check the output in any way.
//!
//! This exists because of some difficulties of running `cargo expand` against the `#[implement]`
//! macro. It's also just really convenient. You can see the output by using `--nocapture` and
//! you'll probably want to restrict the output to a single thread:
//!
//! ```text
//! cargo test -p windows-implement --lib -- --nocapture --test-threads=1
//! ```
use std::io::{Read, Write};
use std::process::{Command, Stdio};
use proc_macro2::TokenStream;
use quote::quote;
fn implement(attributes: TokenStream, item_tokens: TokenStream) -> String {
let out_tokens = crate::implement_core(attributes, item_tokens);
let tokens_string = out_tokens.to_string();
let out_string = rustfmt(&tokens_string);
println!("// output of #[implement] :");
println!();
println!("{}", out_string);
out_string
}
fn rustfmt(input: &str) -> String {
let mut rustfmt = Command::new("rustfmt");
rustfmt.stdin(Stdio::piped());
rustfmt.stdout(Stdio::piped());
rustfmt.stderr(Stdio::inherit());
let mut child = match rustfmt.spawn() {
Ok(c) => c,
Err(e) => {
eprintln!("failed to spawn rustfmt: {e:?}");
return input.to_string();
}
};
let mut stdout = child.stdout.take().unwrap();
// spawn thread to read stdout
let stdout_thread = std::thread::spawn(move || {
let mut buf = String::new();
stdout.read_to_string(&mut buf).unwrap();
buf
});
// write unformatted into stdin
let mut stdin = child.stdin.take().unwrap();
stdin.write_all(input.as_bytes()).unwrap();
drop(stdin);
let stdout_string: String = stdout_thread.join().unwrap();
let exit = child.wait().unwrap();
if !exit.success() {
eprintln!("rustfmt terminated with failure status code");
return input.to_string();
}
stdout_string
}
#[test]
fn simple_type() {
implement(
quote!(IFoo),
quote! {
struct Foo {
x: u32,
}
},
);
}
#[test]
fn zero_sized_type() {
implement(
quote!(IFoo),
quote! {
struct Foo;
},
);
}
#[test]
fn no_interfaces() {
implement(
quote!(),
quote! {
struct Foo {}
},
);
}
#[test]
fn generic_no_lifetime() {
implement(
quote!(IAsyncOperationWithProgress<T, P>, IAsyncInfo),
quote! {
struct OperationWithProgress<T, P>(SyncState<IAsyncOperationWithProgress<T, P>>)
where
T: RuntimeType + 'static,
P: RuntimeType + 'static;
},
);
}
#[test]
fn generic_with_lifetime() {
implement(
quote!(),
quote! {
pub struct Foo<'a> {
pub x: &'a [u8],
}
},
);
}
#[test]
fn tuple_type() {
implement(
quote!(IFoo),
quote! {
struct Foo(pub i32);
},
);
}