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

Overhaul async and future support #3213

Merged
merged 6 commits into from
Aug 22, 2024
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: 2 additions & 0 deletions .github/workflows/clippy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ jobs:
run: cargo clippy -p test_event
- name: Clippy test_extensions
run: cargo clippy -p test_extensions
- name: Clippy test_futures
run: cargo clippy -p test_futures
- name: Clippy test_handles
run: cargo clippy -p test_handles
- name: Clippy test_helpers
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ jobs:
run: cargo test -p test_event --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_extensions
run: cargo test -p test_extensions --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_futures
run: cargo test -p test_futures --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_handles
run: cargo test -p test_handles --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_helpers
Expand Down Expand Up @@ -253,10 +255,10 @@ jobs:
run: cargo test -p test_riddle --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_standalone
run: cargo test -p test_standalone --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_string_param
run: cargo test -p test_string_param --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Clean
run: cargo clean
- name: Test test_string_param
run: cargo test -p test_string_param --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_strings
run: cargo test -p test_strings --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_structs
Expand Down
9 changes: 0 additions & 9 deletions crates/libs/bindgen/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,6 @@ pub struct SignatureParam {
pub kind: SignatureParamKind,
}

#[derive(PartialEq, Eq, Debug)]
pub enum AsyncKind {
None,
Action,
ActionWithProgress,
Operation,
OperationWithProgress,
}

#[derive(Clone, PartialEq, Eq, Default)]
pub struct Guid(
pub u32,
Expand Down
45 changes: 34 additions & 11 deletions crates/libs/bindgen/src/rust/classes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,25 @@ fn gen_class(writer: &Writer, def: metadata::TypeDef) -> TokenStream {
}

let name = to_ident(def.name());
let cfg = cfg::type_def_cfg(writer, def, &[]);
let features = writer.cfg_features(&cfg);
let interfaces = metadata::type_interfaces(&metadata::Type::TypeDef(def, Vec::new()));

// If the default interface is one of the async interfaces then we can simply replace it with a type alias
// for the async interface. This simplifies and reduces code gen and but also provides a more streamlined
// developer experience. This works because the default interface is the same vtable as the class itself.
if let Some(interface) = is_default_async(&interfaces) {
let interface_name = writer.type_name(&interface.ty);

return quote! {
#features
pub type #name = #interface_name;
};
}

let mut methods = quote! {};
let mut method_names = MethodNames::new();

let cfg = cfg::type_def_cfg(writer, def, &[]);
let features = writer.cfg_features(&cfg);

for interface in &interfaces {
if let metadata::Type::TypeDef(def, generics) = &interface.ty {
let mut virtual_names = MethodNames::new();
Expand Down Expand Up @@ -132,14 +144,6 @@ fn gen_class(writer: &Writer, def: metadata::TypeDef) -> TokenStream {
true,
));
tokens.combine(&writer.runtime_name_trait(def, &[], &name, &TokenStream::new(), &features));
tokens.combine(&writer.async_get(
def,
&[],
&name,
&TokenStream::new(),
&TokenStream::new(),
&features,
));
tokens.combine(&iterators::writer(
writer,
def,
Expand Down Expand Up @@ -251,3 +255,22 @@ fn type_is_exclusive(ty: &metadata::Type) -> bool {
_ => false,
}
}

fn is_default_async(interfaces: &[metadata::Interface]) -> Option<&metadata::Interface> {
interfaces.iter().find(|interface| {
if interface.kind == metadata::InterfaceKind::Default {
if let metadata::Type::TypeDef(def, _) = interface.ty {
if matches!(
def.type_name(),
metadata::TypeName::IAsyncAction
| metadata::TypeName::IAsyncActionWithProgress
| metadata::TypeName::IAsyncOperation
| metadata::TypeName::IAsyncOperationWithProgress
) {
return true;
}
}
}
false
})
}
8 changes: 0 additions & 8 deletions crates/libs/bindgen/src/rust/interfaces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,14 +201,6 @@ fn gen_win_interface(writer: &Writer, def: metadata::TypeDef) -> TokenStream {
}
});

tokens.combine(&writer.async_get(
def,
generics,
&ident,
&constraints,
&phantoms,
&features,
));
tokens.combine(&iterators::writer(
writer,
def,
Expand Down
77 changes: 0 additions & 77 deletions crates/libs/bindgen/src/rust/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -634,72 +634,7 @@ impl Writer {
quote! {}
}
}
pub fn async_get(
&self,
def: metadata::TypeDef,
generics: &[metadata::Type],
ident: &TokenStream,
constraints: &TokenStream,
_phantoms: &TokenStream,
features: &TokenStream,
) -> TokenStream {
let mut kind = type_def_async_kind(def);
let mut async_generics = generics.to_vec();

if kind == metadata::AsyncKind::None {
for interface in def.interface_impls().map(move |imp| imp.ty(generics)) {
if let metadata::Type::TypeDef(interface_def, interface_generics) = &interface {
kind = type_def_async_kind(*interface_def);
if kind != metadata::AsyncKind::None {
async_generics = interface_generics.to_vec();
break;
}
}
}
}

if kind == metadata::AsyncKind::None {
quote! {}
} else {
let return_type = match kind {
metadata::AsyncKind::Operation | metadata::AsyncKind::OperationWithProgress => {
self.type_name(&async_generics[0])
}
_ => quote! { () },
};

let handler = match kind {
metadata::AsyncKind::Action => quote! { AsyncActionCompletedHandler },
metadata::AsyncKind::ActionWithProgress => {
quote! { AsyncActionWithProgressCompletedHandler }
}
metadata::AsyncKind::Operation => quote! { AsyncOperationCompletedHandler },
metadata::AsyncKind::OperationWithProgress => {
quote! { AsyncOperationWithProgressCompletedHandler }
}
rest => unimplemented!("{rest:?}"),
};

let namespace = self.namespace("Windows.Foundation");

quote! {
#features
impl<#constraints> #ident {
pub fn get(&self) -> windows_core::Result<#return_type> {
if self.Status()? == #namespace AsyncStatus::Started {
let (_waiter, signaler) = windows_core::imp::Waiter::new()?;
self.SetCompleted(&#namespace #handler::new(move |_sender, _args| {
// Safe because the waiter will only be dropped after being signaled.
unsafe { signaler.signal(); }
Ok(())
}))?;
}
self.GetResults()
}
}
}
}
}
pub fn interface_winrt_trait(
&self,
def: metadata::TypeDef,
Expand Down Expand Up @@ -1463,18 +1398,6 @@ fn gen_const_ptrs(pointers: usize) -> TokenStream {
"*const ".repeat(pointers).into()
}

fn type_def_async_kind(row: metadata::TypeDef) -> metadata::AsyncKind {
match row.type_name() {
metadata::TypeName::IAsyncAction => metadata::AsyncKind::Action,
metadata::TypeName::IAsyncActionWithProgress => metadata::AsyncKind::ActionWithProgress,
metadata::TypeName::IAsyncOperation => metadata::AsyncKind::Operation,
metadata::TypeName::IAsyncOperationWithProgress => {
metadata::AsyncKind::OperationWithProgress
}
_ => metadata::AsyncKind::None,
}
}

fn type_def_is_agile(row: metadata::TypeDef) -> bool {
for attribute in row.attributes() {
match attribute.name() {
Expand Down
Loading