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

Add WinRT noexcept support #3070

Merged
merged 11 commits into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from 7 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 @@ -208,6 +208,8 @@ jobs:
run: cargo clippy -p test_msrv
- name: Clippy test_no_use
run: cargo clippy -p test_no_use
- name: Clippy test_noexcept
run: cargo clippy -p test_noexcept
- name: Clippy test_not_dll
run: cargo clippy -p test_not_dll
- name: Clippy test_query_signature
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ jobs:
run: cargo test -p test_msrv --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_no_use
run: cargo test -p test_no_use --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_noexcept
run: cargo test -p test_noexcept --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_not_dll
run: cargo test -p test_not_dll --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_query_signature
Expand All @@ -256,10 +258,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_structs
run: cargo test -p test_structs --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_sys
Expand Down
4 changes: 4 additions & 0 deletions crates/libs/bindgen/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,10 @@ fn method_def_last_error(row: MethodDef) -> bool {
}
}

pub fn method_def_is_noexcept(method: MethodDef) -> bool {
method.has_attribute("NoExceptionAttribute")
kennykerr marked this conversation as resolved.
Show resolved Hide resolved
}

pub fn type_is_borrowed(ty: &Type) -> bool {
match ty {
Type::TypeDef(row, _) => !type_def_is_blittable(*row),
Expand Down
151 changes: 113 additions & 38 deletions crates/libs/bindgen/src/rust/winrt_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub fn writer(
let features = writer.cfg_features(&cfg);
let args = gen_winrt_abi_args(writer, params);
let params = gen_winrt_params(writer, params);
let noexcept = metadata::method_def_is_noexcept(method);

let return_type_tokens = match &signature.return_type {
metadata::Type::Void => quote! { () },
Expand All @@ -35,6 +36,18 @@ pub fn writer(
}
};

let return_type_tokens = if noexcept {
if metadata::type_is_nullable(&signature.return_type) {
quote! { -> Option<#return_type_tokens> }
} else if signature.return_type == metadata::Type::Void {
quote! {}
} else {
quote! { -> #return_type_tokens }
}
} else {
quote! { -> windows_core::Result<#return_type_tokens> }
};

let return_arg = match &signature.return_type {
metadata::Type::Void => quote! {},
_ => {
Expand All @@ -47,38 +60,68 @@ pub fn writer(
}
};

let vcall = quote! { (windows_core::Interface::vtable(this).#vname)(windows_core::Interface::as_raw(this), #args #return_arg) };

let vcall = match &signature.return_type {
metadata::Type::Void => {
quote! {
(windows_core::Interface::vtable(this).#vname)(windows_core::Interface::as_raw(this), #args).ok()
if noexcept {
quote! {
let hresult__ = #vcall;
debug_assert!(hresult__.0 == 0);
kennykerr marked this conversation as resolved.
Show resolved Hide resolved
}
} else {
quote! {
#vcall.ok()
}
}
}
_ if signature.return_type.is_winrt_array() => {
quote! {
let mut result__ = core::mem::MaybeUninit::zeroed();
(windows_core::Interface::vtable(this).#vname)(windows_core::Interface::as_raw(this), #args #return_arg)
.map(|| result__.assume_init())
if noexcept {
quote! {
let mut result__ = core::mem::MaybeUninit::zeroed();
let hresult__ = #vcall;
debug_assert!(hresult__.0 == 0);
kennykerr marked this conversation as resolved.
Show resolved Hide resolved
result__.assume_init()
}
} else {
quote! {
let mut result__ = core::mem::MaybeUninit::zeroed();
#vcall
.map(|| result__.assume_init())
}
}
}
_ => {
let map = if metadata::type_is_blittable(&signature.return_type) {
quote! { map(||result__) }
} else {
quote! { and_then(|| windows_core::Type::from_abi(result__)) }
};

quote! {
if noexcept {
if metadata::type_is_blittable(&signature.return_type) {
quote! {
let mut result__ = core::mem::zeroed();
let hresult__ = #vcall;
debug_assert!(hresult__.0 == 0);
result__ }
} else {
quote! {
let mut result__ = core::mem::zeroed();
let hresult__ = #vcall;
debug_assert!(hresult__.0 == 0);
core::mem::transmute(result__) }
}
} else if metadata::type_is_blittable(&signature.return_type) {
quote! {
let mut result__ = core::mem::zeroed();
(windows_core::Interface::vtable(this).#vname)(windows_core::Interface::as_raw(this), #args #return_arg)
.#map
#vcall
.map(||result__) }
} else {
quote! { let mut result__ = core::mem::zeroed();
#vcall.and_then(|| windows_core::Type::from_abi(result__)) }
}
}
};

match kind {
metadata::InterfaceKind::Default => quote! {
#features
pub fn #name<#generics>(&self, #params) -> windows_core::Result<#return_type_tokens> #where_clause {
pub fn #name<#generics>(&self, #params) #return_type_tokens #where_clause {
let this = self;
unsafe {
#vcall
Expand All @@ -90,7 +133,7 @@ pub fn writer(
| metadata::InterfaceKind::Overridable => {
quote! {
#features
pub fn #name<#generics>(&self, #params) -> windows_core::Result<#return_type_tokens> #where_clause {
pub fn #name<#generics>(&self, #params) #return_type_tokens #where_clause {
let this = &windows_core::Interface::cast::<#interface_name>(self)?;
unsafe {
#vcall
Expand All @@ -101,7 +144,7 @@ pub fn writer(
metadata::InterfaceKind::Static => {
quote! {
#features
pub fn #name<#generics>(#params) -> windows_core::Result<#return_type_tokens> #where_clause {
pub fn #name<#generics>(#params) #return_type_tokens #where_clause {
Self::#interface_name(|this| unsafe { #vcall })
}
}
Expand Down Expand Up @@ -189,31 +232,53 @@ pub fn gen_upcall(
inner: TokenStream,
this: bool,
) -> TokenStream {
let noexcept = metadata::method_def_is_noexcept(sig.def);

let invoke_args = sig
.params
.iter()
.map(|param| gen_winrt_invoke_arg(writer, param));

let this = if this {
quote! { this, }
} else {
quote! {}
};

match &sig.return_type {
metadata::Type::Void => quote! {
#inner(#this #(#invoke_args,)*).into()
},
metadata::Type::Void => {
if noexcept {
quote! {
#inner(#this #(#invoke_args,)*);
windows_core::HRESULT(0)
}
} else {
quote! {
#inner(#this #(#invoke_args,)*).into()
}
}
}
_ if sig.return_type.is_winrt_array() => {
quote! {
match #inner(#this #(#invoke_args,)*) {
Ok(ok__) => {
let (ok_data__, ok_data_len__) = ok__.into_abi();
// use `core::ptr::write` since `result` could be uninitialized
core::ptr::write(result__, ok_data__);
core::ptr::write(result_size__, ok_data_len__);
windows_core::HRESULT(0)
if noexcept {
quote! {
let ok__ = #inner(#this #(#invoke_args,)*);
let (ok_data__, ok_data_len__) = ok__.into_abi();
core::ptr::write(result__, ok_data__);
kennykerr marked this conversation as resolved.
Show resolved Hide resolved
core::ptr::write(result_size__, ok_data_len__);
windows_core::HRESULT(0)
}
} else {
quote! {
match #inner(#this #(#invoke_args,)*) {
Ok(ok__) => {
let (ok_data__, ok_data_len__) = ok__.into_abi();
// use `core::ptr::write` since `result` could be uninitialized
core::ptr::write(result__, ok_data__);
core::ptr::write(result_size__, ok_data_len__);
windows_core::HRESULT(0)
}
Err(err) => err.into()
}
Err(err) => err.into()
}
}
}
Expand All @@ -224,15 +289,25 @@ pub fn gen_upcall(
quote! { core::mem::forget(ok__); }
};

quote! {
match #inner(#this #(#invoke_args,)*) {
Ok(ok__) => {
// use `core::ptr::write` since `result` could be uninitialized
core::ptr::write(result__, core::mem::transmute_copy(&ok__));
#forget
windows_core::HRESULT(0)
if noexcept {
quote! {
let ok__ = #inner(#this #(#invoke_args,)*);
// use `core::ptr::write` since `result` could be uninitialized
core::ptr::write(result__, core::mem::transmute_copy(&ok__));
#forget
windows_core::HRESULT(0)
}
} else {
quote! {
match #inner(#this #(#invoke_args,)*) {
Ok(ok__) => {
// use `core::ptr::write` since `result` could be uninitialized
core::ptr::write(result__, core::mem::transmute_copy(&ok__));
#forget
windows_core::HRESULT(0)
}
Err(err) => err.into()
}
Err(err) => err.into()
}
}
}
Expand Down
18 changes: 16 additions & 2 deletions crates/libs/bindgen/src/rust/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1284,12 +1284,14 @@ impl Writer {
.contains(metadata::TypeAttributes::WindowsRuntime)
{
let is_delegate = def.kind() == metadata::TypeKind::Delegate;
let noexcept = metadata::method_def_is_noexcept(signature.def);

let params = signature
.params
.iter()
.map(|p| self.winrt_produce_type(p, !is_delegate));

let return_type = match &signature.return_type {
let return_type_tokens = match &signature.return_type {
metadata::Type::Void => quote! { () },
kennykerr marked this conversation as resolved.
Show resolved Hide resolved
_ => {
let tokens = self.type_name(&signature.return_type);
Expand All @@ -1302,13 +1304,25 @@ impl Writer {
}
};

let return_type_tokens = if noexcept {
if metadata::type_is_nullable(&signature.return_type) {
quote! { -> Option<#return_type_tokens> }
} else if signature.return_type == metadata::Type::Void {
quote! {}
} else {
quote! { -> #return_type_tokens }
}
} else {
quote! { -> windows_core::Result<#return_type_tokens> }
};

let this = if is_delegate {
quote! {}
} else {
quote! { &self, }
};

quote! { (#this #(#params),*) -> windows_core::Result<#return_type> }
quote! { (#this #(#params),*) #return_type_tokens }
} else {
let signature_kind = signature.kind();
let mut params = quote! {};
Expand Down
12 changes: 4 additions & 8 deletions crates/libs/windows/src/Windows/UI/UIAutomation/Core/impl.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pub trait ICoreAutomationConnectionBoundObjectProvider_Impl: Sized {
fn IsComThreadingRequired(&self) -> windows_core::Result<bool>;
fn IsComThreadingRequired(&self) -> bool;
kennykerr marked this conversation as resolved.
Show resolved Hide resolved
}
impl windows_core::RuntimeName for ICoreAutomationConnectionBoundObjectProvider {
const NAME: &'static str = "Windows.UI.UIAutomation.Core.ICoreAutomationConnectionBoundObjectProvider";
Expand All @@ -9,13 +9,9 @@ impl ICoreAutomationConnectionBoundObjectProvider_Vtbl {
unsafe extern "system" fn IsComThreadingRequired<Identity: windows_core::IUnknownImpl<Impl = Impl>, Impl: ICoreAutomationConnectionBoundObjectProvider_Impl, const OFFSET: isize>(this: *mut core::ffi::c_void, result__: *mut bool) -> windows_core::HRESULT {
let this = (this as *const *const ()).offset(OFFSET) as *const Identity;
let this = (*this).get_impl();
match ICoreAutomationConnectionBoundObjectProvider_Impl::IsComThreadingRequired(this) {
Ok(ok__) => {
core::ptr::write(result__, core::mem::transmute_copy(&ok__));
windows_core::HRESULT(0)
}
Err(err) => err.into(),
}
let ok__ = ICoreAutomationConnectionBoundObjectProvider_Impl::IsComThreadingRequired(this);
core::ptr::write(result__, core::mem::transmute_copy(&ok__));
windows_core::HRESULT(0)
}
Self {
base__: windows_core::IInspectable_Vtbl::new::<Identity, ICoreAutomationConnectionBoundObjectProvider, OFFSET>(),
Expand Down
6 changes: 4 additions & 2 deletions crates/libs/windows/src/Windows/UI/UIAutomation/Core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ impl core::ops::Deref for ICoreAutomationConnectionBoundObjectProvider {
}
windows_core::imp::interface_hierarchy!(ICoreAutomationConnectionBoundObjectProvider, windows_core::IUnknown, windows_core::IInspectable);
impl ICoreAutomationConnectionBoundObjectProvider {
pub fn IsComThreadingRequired(&self) -> windows_core::Result<bool> {
pub fn IsComThreadingRequired(&self) -> bool {
let this = self;
unsafe {
let mut result__ = core::mem::zeroed();
(windows_core::Interface::vtable(this).IsComThreadingRequired)(windows_core::Interface::as_raw(this), &mut result__).map(|| result__)
let hresult__ = (windows_core::Interface::vtable(this).IsComThreadingRequired)(windows_core::Interface::as_raw(this), &mut result__);
debug_assert!(hresult__.0 == 0);
result__
}
}
}
Expand Down
12 changes: 8 additions & 4 deletions crates/libs/windows/src/Windows/UI/UIAutomation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,13 @@ pub struct IAutomationTextRange_Vtbl {
pub struct AutomationConnection(windows_core::IUnknown);
windows_core::imp::interface_hierarchy!(AutomationConnection, windows_core::IUnknown, windows_core::IInspectable);
impl AutomationConnection {
pub fn IsRemoteSystem(&self) -> windows_core::Result<bool> {
pub fn IsRemoteSystem(&self) -> bool {
let this = self;
unsafe {
let mut result__ = core::mem::zeroed();
(windows_core::Interface::vtable(this).IsRemoteSystem)(windows_core::Interface::as_raw(this), &mut result__).map(|| result__)
let hresult__ = (windows_core::Interface::vtable(this).IsRemoteSystem)(windows_core::Interface::as_raw(this), &mut result__);
debug_assert!(hresult__.0 == 0);
result__
}
}
pub fn AppUserModelId(&self) -> windows_core::Result<windows_core::HSTRING> {
Expand Down Expand Up @@ -108,11 +110,13 @@ unsafe impl Sync for AutomationConnectionBoundObject {}
pub struct AutomationElement(windows_core::IUnknown);
windows_core::imp::interface_hierarchy!(AutomationElement, windows_core::IUnknown, windows_core::IInspectable);
impl AutomationElement {
pub fn IsRemoteSystem(&self) -> windows_core::Result<bool> {
pub fn IsRemoteSystem(&self) -> bool {
let this = self;
unsafe {
let mut result__ = core::mem::zeroed();
(windows_core::Interface::vtable(this).IsRemoteSystem)(windows_core::Interface::as_raw(this), &mut result__).map(|| result__)
let hresult__ = (windows_core::Interface::vtable(this).IsRemoteSystem)(windows_core::Interface::as_raw(this), &mut result__);
debug_assert!(hresult__.0 == 0);
result__
}
}
pub fn AppUserModelId(&self) -> windows_core::Result<windows_core::HSTRING> {
Expand Down
11 changes: 11 additions & 0 deletions crates/tests/noexcept/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "test_noexcept"
version = "0.0.0"
edition = "2021"
publish = false

[dependencies.windows-core]
path = "../../libs/core"

[build-dependencies.windows-bindgen]
path = "../../libs/bindgen"
Loading
Loading