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

Basic delegate support #130

Merged
merged 25 commits into from
May 7, 2020
Merged
2 changes: 1 addition & 1 deletion crates/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub fn import(stream: TokenStream) -> TokenStream {
let stage = TypeStage::from_limits(reader, &limits);
let tree = stage.into_tree();
let stream = tree.to_tokens();

//std::fs::write(r"c:\git\rust\dump.rs", stream.to_string()).unwrap();
stream.into()
}

Expand Down
4 changes: 0 additions & 4 deletions crates/winmd/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@ fn format_ident(name: &str) -> proc_macro2::Ident {
}
}

fn format_abi_ident(name: &str) -> proc_macro2::Ident {
quote::format_ident!("abi_{}", name)
}

#[cfg(target_pointer_width = "64")]
const SYSTEM32: &str = "System32";

Expand Down
153 changes: 148 additions & 5 deletions crates/winmd/src/types/delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,38 @@ impl Delegate {
pub fn to_tokens(&self) -> TokenStream {
let definition = self.name.to_definition_tokens(&self.name.namespace);
let abi_definition = self.name.to_abi_definition_tokens(&self.name.namespace);
let fn_constraint = self.to_fn_constraint_tokens();
let impl_definition = self.to_impl_definition_tokens(&fn_constraint);
let name = self.name.to_tokens(&self.name.namespace);
let abi_name = self.name.to_abi_tokens(&self.name.namespace);
let impl_name = self.to_impl_name_tokens();
let phantoms = self.name.phantoms();
let constraints = self.name.constraints();
let method = self.method.to_default_tokens(&self.name.namespace);
let abi_method = self.method.to_abi_tokens(&self.name, &self.name.namespace);
let guid = self.guid.to_tokens();
let invoke_sig = self
.method
.to_abi_impl_tokens(&self.name, &self.name.namespace);
let invoke_args = self
.method
.params
.iter()
.map(|param| param.to_invoke_arg_tokens());

quote! {
#[repr(transparent)]
#[derive(Default)]
pub struct #definition where #constraints {
ptr: ::winrt::IUnknown,
ptr: ::winrt::ComPtr<#name>,
#phantoms
}
impl<#constraints> #name {
#method
pub fn new<#fn_constraint>(invoke: F) -> Self {
#impl_name::new(invoke)
}
}
unsafe impl<#constraints> ::winrt::ComInterface for #name {
type VTable = #abi_definition;
const IID: ::winrt::Guid = ::winrt::Guid::from_values(#guid);
Expand All @@ -58,19 +77,143 @@ impl Delegate {
}
#[repr(C)]
pub struct #abi_definition where #constraints {
__base: [usize; 6],
pub unknown_query_interface: extern "system" fn(::winrt::RawComPtr<::winrt::IUnknown>, &::winrt::Guid, *mut ::winrt::RawPtr) -> ::winrt::ErrorCode,
pub unknown_add_ref: extern "system" fn(::winrt::RawComPtr<::winrt::IUnknown>) -> u32,
pub unknown_release: extern "system" fn(::winrt::RawComPtr<::winrt::IUnknown>) -> u32,
#abi_method
#phantoms
}
unsafe impl<#constraints> ::winrt::RuntimeType for #name {
type Abi = ::winrt::RawPtr;
type Abi = ::winrt::RawComPtr<Self>;
fn abi(&self) -> Self::Abi {
<::winrt::IUnknown as ::winrt::ComInterface>::as_raw(&self.ptr) as Self::Abi
<::winrt::ComPtr<Self> as ::winrt::ComInterface>::as_raw(&self.ptr)
}
fn set_abi(&mut self) -> *mut Self::Abi {
self.ptr.set_abi() as _
self.ptr.set_abi()
}
}
#[repr(C)]
struct #impl_definition where #constraints {
vtable: *const #abi_definition,
count: ::winrt::RefCount,
invoke: F,
}
impl<#constraints #fn_constraint> #impl_name {
const VTABLE: #abi_definition = #abi_name {
unknown_query_interface: #impl_name::unknown_query_interface,
unknown_add_ref: #impl_name::unknown_add_ref,
unknown_release: #impl_name::unknown_release,
invoke: #impl_name::invoke,
#phantoms
};
pub fn new(invoke: F) -> #name {
let value = Self {
vtable: &Self::VTABLE,
count: ::winrt::RefCount::new(1),
invoke,
};
unsafe {
let mut result: #name = std::mem::zeroed();
*<#name as ::winrt::RuntimeType>::set_abi(&mut result) = ::std::boxed::Box::into_raw(::std::boxed::Box::new(value)) as *const *const #abi_definition;
result
}
}
extern "system" fn unknown_query_interface(
this: ::winrt::RawComPtr<::winrt::IUnknown>,
iid: &::winrt::Guid,
interface: *mut ::winrt::RawPtr,
) -> ::winrt::ErrorCode {
unsafe {
let this = this as *const Self as *mut Self;

if *iid == <#name as ::winrt::ComInterface>::IID
|| *iid == <::winrt::IUnknown as ::winrt::ComInterface>::IID
|| *iid == <::winrt::IAgileObject as ::winrt::ComInterface>::IID
{
*interface = this as ::winrt::RawPtr;
(*this).count.add_ref();
return ::winrt::ErrorCode(0);
}

*interface = std::ptr::null_mut();
::winrt::ErrorCode(0x80004002)
}
}
extern "system" fn unknown_add_ref(this: ::winrt::RawComPtr<::winrt::IUnknown>) -> u32 {
unsafe {
let this = this as *const Self as *mut Self;
(*this).count.add_ref()
}
}
extern "system" fn unknown_release(this: ::winrt::RawComPtr<::winrt::IUnknown>) -> u32 {
unsafe {
let this = this as *const Self as *mut Self;
let remaining = (*this).count.release();

if remaining == 0 {
Box::from_raw(this);
}

remaining
}
}
#invoke_sig {
unsafe {
let this = this as *const Self as *mut Self;
((*this).invoke)(#(#invoke_args,)*).into()
}
}
}
}
}

fn to_fn_constraint_tokens(&self) -> TokenStream {
let params = self
.method
.params
.iter()
.map(|param| param.to_fn_tokens(&self.name.namespace));

let return_type = if let Some(return_type) = &self.method.return_type {
return_type.to_return_tokens(&self.name.namespace)
} else {
quote! { () }
};

quote! { F: FnMut(#(#params)*) -> ::winrt::Result<#return_type> }
}

fn to_impl_definition_tokens(&self, fn_constraint: &TokenStream) -> TokenStream {
if self.name.generics.is_empty() {
let name = format_impl_ident(&self.name.name);
quote! { #name<#fn_constraint> }
} else {
let name = format_impl_ident(&self.name.name[..self.name.name.len() - 2]);
let generics = self
.name
.generics
.iter()
.map(|g| g.to_tokens(&self.name.namespace));
quote! { #name<#(#generics,)* #fn_constraint> }
}
}

fn to_impl_name_tokens(&self) -> TokenStream {
if self.name.generics.is_empty() {
let name = format_impl_ident(&self.name.name);
quote! { #name::<F> }
} else {
let name = format_impl_ident(&self.name.name[..self.name.name.len() - 2]);
let generics = self
.name
.generics
.iter()
.map(|g| g.to_tokens(&self.name.namespace));
quote! { #name::<#(#generics,)* F> }
}
}
}

fn format_impl_ident(name: &str) -> proc_macro2::Ident {
quote::format_ident!("impl_{}", name)
}
7 changes: 6 additions & 1 deletion crates/winmd/src/types/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,12 @@ impl Interface {
}
#[repr(C)]
pub struct #abi_definition where #constraints {
__base: [usize; 6],
pub unknown_query_interface: extern "system" fn(::winrt::RawComPtr<::winrt::IUnknown>, &::winrt::Guid, *mut ::winrt::RawPtr) -> ::winrt::ErrorCode,
pub unknown_add_ref: extern "system" fn(::winrt::RawComPtr<::winrt::IUnknown>) -> u32,
pub unknown_release: extern "system" fn(::winrt::RawComPtr<::winrt::IUnknown>) -> u32,
pub inspectable_iids: extern "system" fn(::winrt::RawComPtr<::winrt::Object>, *mut u32, *mut *mut ::winrt::Guid) -> ::winrt::ErrorCode,
pub inspectable_type_name: extern "system" fn(::winrt::RawComPtr<::winrt::Object>, *mut <::winrt::HString as ::winrt::RuntimeType>::Abi) -> ::winrt::ErrorCode,
pub inspectable_trust_level: extern "system" fn(::winrt::RawComPtr<::winrt::Object>, *mut i32) -> ::winrt::ErrorCode,
#abi_methods
#phantoms
}
Expand Down
20 changes: 19 additions & 1 deletion crates/winmd/src/types/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl Method {
let return_type = if blob.read_expected(0x01) {
None
} else {
let name = String::new();
let name = "__result".to_owned();
let array = blob.peek_unsigned().0 == 0x1D;
let kind = TypeKind::from_blob(&mut blob, generics);
let input = false;
Expand Down Expand Up @@ -147,6 +147,24 @@ impl Method {
}
}

pub fn to_abi_impl_tokens(&self, self_name: &TypeName, calling_namespace: &str) -> TokenStream {
let abi_name = self_name.to_abi_tokens(calling_namespace);
let name = format_ident(&self.name);
let params = self
.params
.iter()
.chain(self.return_type.iter())
.map(|param| {
let name = format_ident(&param.name);
let abi = param.to_abi_tokens(calling_namespace);
quote! { #name: #abi }
});

quote! {
extern "system" fn #name(this: *const *const #abi_name, #(#params)*) -> ::winrt::ErrorCode
}
}

fn to_param_tokens(&self, calling_namespace: &str) -> TokenStream {
TokenStream::from_iter(
self.params
Expand Down
49 changes: 49 additions & 0 deletions crates/winmd/src/types/param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,36 @@ impl Param {
}
}

pub fn to_fn_tokens(&self, calling_namespace: &str) -> TokenStream {
let tokens = self.kind.to_tokens(calling_namespace);

if self.array {
if self.input {
quote! { &[#tokens], }
} else if self.by_ref {
quote! { &mut ::winrt::Array<#tokens>, }
} else {
quote! { &mut [#tokens], }
}
} else if self.input {
match self.kind {
TypeKind::String
| TypeKind::Object
| TypeKind::Guid
| TypeKind::Class(_)
| TypeKind::Interface(_)
| TypeKind::Struct(_)
| TypeKind::Delegate(_)
| TypeKind::Generic(_) => {
quote! { &#tokens, }
}
_ => quote! { #tokens, },
}
} else {
quote! { &mut #tokens, }
}
}

pub fn to_return_tokens(&self, calling_namespace: &str) -> TokenStream {
let tokens = self.kind.to_tokens(calling_namespace);

Expand Down Expand Up @@ -117,4 +147,23 @@ impl Param {
quote! { ::winrt::RuntimeType::set_abi(#name), }
}
}

pub fn to_invoke_arg_tokens(&self) -> TokenStream {
let name = format_ident(&self.name);

if self.array {
// TODO: delegate with array parameters are challenging to say the least.
// I'll get to them shortly.
panic!("array");
} else if self.input {
match self.kind {
TypeKind::Enum(_) => quote! { *::winrt::RuntimeType::from_abi(&#name) },
_ => quote! { ::winrt::RuntimeType::from_abi(&#name) },
}
} else if self.kind.blittable() {
quote! { #name, }
} else {
quote! { ::winrt::RuntimeType::from_mut_abi(#name) }
}
}
}
25 changes: 8 additions & 17 deletions crates/winmd/src/types/type_kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,32 +218,23 @@ impl TypeKind {
Self::U64 => quote! { u64, },
Self::F32 => quote! { f32, },
Self::F64 => quote! { f64, },
Self::Guid => quote! { ::winrt::Guid, },
Self::String => {
quote! { <::winrt::HString as ::winrt::RuntimeType>::Abi, }
}
Self::Object => {
quote! { <::winrt::Object as ::winrt::RuntimeType>::Abi, }
}
Self::Guid => quote! { ::winrt::Guid, },
Self::Class(c) => {
let name = c.to_tokens(calling_namespace);
quote! { <#name as ::winrt::RuntimeType>::Abi, }
}
Self::Interface(i) => {
let name = i.to_tokens(calling_namespace);
quote! { <#name as ::winrt::RuntimeType>::Abi, }
}
Self::Enum(name) => {
let name = name.to_tokens(calling_namespace);
Self::Generic(name) => {
let name = format_ident(name);
quote! { <#name as ::winrt::RuntimeType>::Abi, }
}
Self::Struct(name) => {
Self::Class(name)
| Self::Interface(name)
| Self::Delegate(name)
| Self::Enum(name)
| Self::Struct(name) => {
let name = name.to_tokens(calling_namespace);
quote! { #name, }
}
Self::Delegate(_) => quote! { ::winrt::RawPtr, },
Self::Generic(name) => {
let name = format_ident(name);
quote! { <#name as ::winrt::RuntimeType>::Abi, }
}
}
Expand Down
Loading