This commit is contained in:
Ryan Levick 2020-07-16 19:06:28 +02:00
Родитель 184ab0e534
Коммит 55e77b52ab
15 изменённых файлов: 144 добавлений и 162 удалений

Просмотреть файл

@ -15,6 +15,9 @@ readme = "./README.md"
[dependencies]
com_macros = { version = "0.2", path = "macros" }
[features]
production = []
[[test]]
name = "tests"
path = "tests/progress.rs"

Просмотреть файл

@ -5,7 +5,7 @@ authors = ["Microsoft Corp"]
edition = "2018"
[dependencies]
com = { path = "../../.." }
com = { path = "../../..", features = ["production"] }
interface = { path = "../interface" }
[lib]

Просмотреть файл

@ -6,6 +6,6 @@ pub fn generate(struct_item: &CoClass) -> HelperTokenStream {
let struct_ident = &struct_item.name;
quote! {
unsafe impl com::CoClass for #struct_ident {}
unsafe impl com::production::CoClass for #struct_ident {}
}
}

Просмотреть файл

@ -1,7 +1,6 @@
use super::CoClass;
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Fields, Ident, ItemStruct};
/// The COM class object
///
@ -18,8 +17,10 @@ pub fn generate(co_class: &CoClass) -> TokenStream {
let base_fields = gen_base_fields(&co_class.interfaces);
let ref_count_field = gen_ref_count_field();
let user_fields = &co_class.fields;
let docs = &co_class.docs;
quote!(
#(#docs)*
#[repr(C)]
#vis struct #struct_ident {
#base_fields

Просмотреть файл

@ -1,7 +1,7 @@
use super::CoClass;
use proc_macro2::TokenStream as HelperTokenStream;
use quote::{format_ident, quote};
use syn::{Fields, Ident, ItemStruct};
use syn::{Ident, ItemStruct};
pub fn generate(co_class: &CoClass) -> HelperTokenStream {
let allocate_fn = gen_allocate_fn(co_class);

Просмотреть файл

@ -8,26 +8,32 @@ use syn::Ident;
/// Takes into account the base interfaces exposed, as well as
/// any interfaces exposed through an aggregated object.
pub fn generate(co_class: &CoClass) -> TokenStream {
let struct_ident = &co_class.name;
let ident = quote::format_ident!("{}_IUNKNOWN", co_class.name.to_string().to_uppercase());
let add_ref = gen_add_ref();
let release = gen_release(&co_class.interfaces, struct_ident);
let query_interface = gen_query_interface(&co_class.interfaces);
let add_ref = gen_add_ref(&co_class.name);
let release = gen_release(&co_class.interfaces, &co_class.name);
let query_interface = gen_query_interface(&co_class.name, &co_class.interfaces);
quote!(
impl #struct_ident {
quote! {
const #ident: <::com::interfaces::IUnknown as ::com::ComInterface>::VTable = {
#add_ref
#release
#query_interface
}
)
::com::interfaces::IUnknown::VTable {
AddRef: add_ref ,
Release: release,
QueryInterface: query_interface,
}
};
}
}
pub fn gen_add_ref() -> TokenStream {
pub fn gen_add_ref(name: &Ident) -> TokenStream {
let add_ref_implementation = gen_add_ref_implementation();
quote! {
unsafe extern "stdcall" fn add_ref(this_ptr: *mut *mut Self) -> u32 {
extern "stdcall" fn add_ref(this_ptr: *mut *mut #name) -> u32 {
assert!(!this_ptr.is_null());
let this = &(*(*this_ptr));
#add_ref_implementation
@ -54,7 +60,7 @@ pub fn gen_release(interface_idents: &[syn::Path], name: &Ident) -> TokenStream
let release_drops = gen_release_drops(interface_idents, name);
quote! {
unsafe extern "stdcall" fn release(this_ptr: *mut *mut Self) -> u32 {
unsafe extern "stdcall" fn release(this_ptr: *mut *mut #name) -> u32 {
assert!(!this_ptr.is_null());
let this = &(*(*this_ptr));
#release_decrement
@ -101,15 +107,12 @@ pub fn gen_release_drops(interface_idents: &[syn::Path], name: &Ident) -> TokenS
}
fn gen_vptr_drops(interface_idents: &[syn::Path]) -> TokenStream {
let vptr_drops = interface_idents
.iter()
.enumerate()
.map(|(index, interface)| {
let vptr_field_ident = quote::format_ident!("__{}", index);
quote!(
Box::from_raw(this.#vptr_field_ident.as_ptr());
)
});
let vptr_drops = interface_idents.iter().enumerate().map(|(index, _)| {
let vptr_field_ident = quote::format_ident!("__{}", index);
quote!(
Box::from_raw(this.#vptr_field_ident.as_ptr());
)
});
quote!(#(#vptr_drops)*)
}
@ -120,13 +123,13 @@ fn gen_com_object_drop(name: &Ident) -> TokenStream {
)
}
pub fn gen_query_interface(interface_idents: &[syn::Path]) -> TokenStream {
pub fn gen_query_interface(name: &Ident, interface_idents: &[syn::Path]) -> TokenStream {
// Generate match arms for implemented interfaces
let base_match_arms = gen_base_match_arms(interface_idents);
quote! {
unsafe extern "stdcall" fn query_interface(
this_ptr: *mut *mut Self,
this_ptr: *mut *mut #name,
riid: *const com::sys::IID,
ppv: *mut *mut std::ffi::c_void
) -> com::sys::HRESULT {
@ -141,7 +144,7 @@ pub fn gen_query_interface(interface_idents: &[syn::Path]) -> TokenStream {
return com::sys::E_NOINTERFACE;
}
Self::add_ref(this_ptr);
#name::add_ref(this_ptr);
com::sys::NOERROR
}
}

Просмотреть файл

@ -1,5 +1,4 @@
use proc_macro2::{Ident, TokenStream};
use syn::{AttributeArgs, ItemStruct};
use std::iter::FromIterator;

Просмотреть файл

@ -1,4 +1,3 @@
use super::vtable_macro;
use super::Interface;
use crate::com_interface::{iid, vtable};
@ -9,7 +8,7 @@ pub fn generate(interface: &Interface) -> TokenStream {
let interface_ident = &interface.name;
let vtable_ident = vtable::ident(&interface_ident.to_string());
let iid_ident = iid::ident(interface_ident);
let vtable_macro = vtable_macro::ident(&interface_ident);
// let vtable_macro = vtable_macro::ident(&interface_ident);
let parent = if let Some(p) = &interface.parent {
quote! { #p }
} else {

Просмотреть файл

@ -1,5 +1,5 @@
use quote::format_ident;
use syn::{AttributeArgs, Ident, Meta, NestedMeta};
use syn::Ident;
pub fn class_factory_ident(class_ident: &Ident) -> Ident {
format_ident!("{}ClassFactory", class_ident)
@ -8,44 +8,3 @@ pub fn class_factory_ident(class_ident: &Ident) -> Ident {
pub fn ref_count_ident() -> Ident {
format_ident!("__refcnt")
}
pub fn vptr_field_ident(interface_ident: &Ident) -> Ident {
format_ident!("__{}vptr", interface_ident.to_string().to_lowercase())
}
pub fn base_interface_idents(attr_args: &AttributeArgs) -> Vec<Ident> {
let mut base_interface_idents = Vec::new();
for attr_arg in attr_args {
if let NestedMeta::Meta(Meta::List(ref attr)) = attr_arg {
if attr
.path
.segments
.last()
.expect("Invalid attribute syntax")
.ident
!= "implements"
{
continue;
}
for item in &attr.nested {
if let NestedMeta::Meta(Meta::Path(p)) = item {
assert!(
p.segments.len() == 1,
"Incapable of handling multiple path segments yet."
);
base_interface_idents.push(
p.segments
.last()
.expect("Implemented interface is empty path")
.ident
.clone(),
);
}
}
}
}
base_interface_idents
}

44
src/com_interface.rs Normal file
Просмотреть файл

@ -0,0 +1,44 @@
use crate::interfaces::IUnknown;
use crate::sys::IID;
/// A COM compliant interface pointer
///
/// # Safety
///
/// The struct implementing this trait must provide a valid vtable as the
/// associated VTable type. A vtable is valid if:
/// * it is `#[repr(C)]`
/// * the type only contains `extern "stdcall" fn" definitions
///
/// The implementor must be a transparrently equivalent to a valid interface pointer
/// for the interface `T`. An interface pointer as the name suggests points to an
/// interface. A valid interface is itself trivial castable to a `*mut T::VTable`.
/// In other words, the implementing type must also be equal to `*mut *const T::VTable`
pub unsafe trait ComInterface: Sized + 'static {
/// A COM compatible V-Table
type VTable;
/// The interface that this interface inherits from
type Super: ComInterface;
/// The associated id for this interface
const IID: IID;
/// Check whether a given IID is in the inheritance hierarchy of this interface
fn is_iid_in_inheritance_chain(riid: &IID) -> bool {
riid == &Self::IID
|| (Self::IID != <IUnknown as ComInterface>::IID
&& <Self::Super as ComInterface>::is_iid_in_inheritance_chain(riid))
}
/// Cast the interface pointer to a pointer to IUnknown.
fn as_iunknown(&self) -> &IUnknown {
unsafe { std::mem::transmute(self) }
}
/// Cast the COM interface pointer to a raw pointer
///
/// The returned pointer is only guranteed valid for as long
/// as the reference to self id valid.
fn as_raw(&self) -> std::ptr::NonNull<std::ptr::NonNull<Self::VTable>> {
unsafe { std::mem::transmute_copy(self) }
}
}

Просмотреть файл

@ -19,109 +19,29 @@
#![deny(missing_docs)]
mod abi_transferable;
mod com_interface;
pub mod interfaces;
#[doc(hidden)]
pub mod offset;
mod param;
#[doc(hidden)]
pub mod registration;
pub mod runtime;
pub mod sys;
#[cfg(feature = "production")]
/// Functionality for producing COM classes
pub mod production;
#[doc(inline)]
pub use abi_transferable::AbiTransferable;
use interfaces::IUnknown;
#[doc(inline)]
pub use com_interface::ComInterface;
#[doc(inline)]
pub use param::Param;
#[doc(inline)]
pub use sys::{CLSID, IID};
/// A COM compliant interface pointer
///
/// # Safety
///
/// The struct implementing this trait must provide a valid vtable as the
/// associated VTable type. A vtable is valid if:
/// * it is `#[repr(C)]`
/// * the type only contains `extern "stdcall" fn" definitions
///
/// The implementor must be a transparrently equivalent to a valid interface pointer
/// for the interface `T`. An interface pointer as the name suggests points to an
/// interface. A valid interface is itself trivial castable to a `*mut T::VTable`.
/// In other words, the implementing type must also be equal to `*mut *const T::VTable`
pub unsafe trait ComInterface: Sized + 'static {
/// A COM compatible V-Table
type VTable;
/// The interface that this interface inherits from
type Super: ComInterface;
/// The associated id for this interface
const IID: IID;
pub use com_macros::com_interface;
/// Check whether a given IID is in the inheritance hierarchy of this interface
fn is_iid_in_inheritance_chain(riid: &IID) -> bool {
riid == &Self::IID
|| (Self::IID != <IUnknown as ComInterface>::IID
&& <Self::Super as ComInterface>::is_iid_in_inheritance_chain(riid))
}
/// Cast the interface pointer to a pointer to IUnknown.
fn as_iunknown(&self) -> &IUnknown {
unsafe { std::mem::transmute(self) }
}
/// Cast the COM interface pointer to a raw pointer
///
/// The returned pointer is only guranteed valid for as long
/// as the reference to self id valid.
fn as_raw(&self) -> std::ptr::NonNull<std::ptr::NonNull<Self::VTable>> {
unsafe { std::mem::transmute_copy(self) }
}
}
/// A COM compliant class
///
/// # Safety
///
/// The implementing struct must have the following properties:
/// * it is `#[repr(C)]`
/// * The first fields of the struct are pointers to the backing VTables for
/// each of the COM Interfaces the class implements
pub unsafe trait CoClass {}
/// A COM interface that will be exposed in a COM server
pub trait ProductionComInterface<T>: ComInterface {
/// Get the vtable for a particular COM interface
fn vtable<O: offset::Offset>() -> Self::VTable;
}
#[doc(hidden)]
#[macro_export]
macro_rules! vtable {
($class:ident: $interface:ident, $offset:ident) => {
<$interface as $crate::ProductionComInterface<$class>>::vtable::<$crate::offset::$offset>();
};
($class:ident: $interface:ident, 4usize) => {
$crate::vtable!($class: $interface, Four)
};
($class:ident: $interface:ident, 3usize) => {
$crate::vtable!($class: $interface, Three)
};
($class:ident: $interface:ident, 2usize) => {
$crate::vtable!($class: $interface, Two)
};
($class:ident: $interface:ident, 1usize) => {
$crate::vtable!($class: $interface, One)
};
($class:ident: $interface:ident, 0usize) => {
$crate::vtable!($class: $interface, Zero)
};
($class:ident: $interface:ident) => {
$crate::vtable!($class: $interface, Zero)
};
}
#[doc(hidden)]
pub use com_macros::{co_class, com_interface, VTable};
#[cfg(feature = "production")]
pub use com_macros::{co_class, VTable};
// this allows for the crate to refer to itself as `com` to keep macros consistent
// whether they are used by some other crate or internally

8
src/production.rs Normal file
Просмотреть файл

@ -0,0 +1,8 @@
mod co_class;
#[doc(hidden)]
pub mod offset;
#[doc(hidden)]
pub mod registration;
pub use co_class::CoClass;
pub use co_class::ProductionComInterface;

Просмотреть файл

@ -0,0 +1,46 @@
use super::offset;
use crate::ComInterface;
/// A COM compliant class
///
/// # Safety
///
/// The implementing struct must have the following properties:
/// * it is `#[repr(C)]`
/// * The first fields of the struct are pointers to the backing VTables for
/// each of the COM Interfaces the class implements
pub unsafe trait CoClass {}
/// A COM interface that will be exposed in a COM server
pub trait ProductionComInterface<T>: ComInterface {
/// Get the vtable for a particular COM interface
fn vtable<O: offset::Offset>() -> Self::VTable;
}
#[doc(hidden)]
#[macro_export]
macro_rules! vtable {
($class:ident: $interface:ident, $offset:ident) => {
<$interface as $crate::production::ProductionComInterface<$class>>::vtable::<
$crate::production::offset::$offset,
>();
};
($class:ident: $interface:ident, 4usize) => {
$crate::vtable!($class: $interface, Four)
};
($class:ident: $interface:ident, 3usize) => {
$crate::vtable!($class: $interface, Three)
};
($class:ident: $interface:ident, 2usize) => {
$crate::vtable!($class: $interface, Two)
};
($class:ident: $interface:ident, 1usize) => {
$crate::vtable!($class: $interface, One)
};
($class:ident: $interface:ident, 0usize) => {
$crate::vtable!($class: $interface, Zero)
};
($class:ident: $interface:ident) => {
$crate::vtable!($class: $interface, Zero)
};
}

Просмотреть файл

Просмотреть файл