From 21d17ab9985ab9f80be57ef17a64c80420421e3f Mon Sep 17 00:00:00 2001 From: Kenny Kerr Date: Thu, 6 Jun 2024 17:07:15 -0700 Subject: [PATCH] Add WinRT `noexcept` support (#3070) --- .github/workflows/clippy.yml | 2 + .github/workflows/test.yml | 6 +- crates/libs/bindgen/src/metadata.rs | 4 + crates/libs/bindgen/src/rust/winrt_methods.rs | 151 +++-- crates/libs/bindgen/src/rust/writer.rs | 33 +- .../src/Windows/UI/UIAutomation/Core/impl.rs | 12 +- .../src/Windows/UI/UIAutomation/Core/mod.rs | 6 +- .../src/Windows/UI/UIAutomation/mod.rs | 12 +- crates/tests/noexcept/Cargo.toml | 17 + crates/tests/noexcept/build.rs | 59 ++ crates/tests/noexcept/src/bindings.rs | 590 ++++++++++++++++++ crates/tests/noexcept/src/interop.cpp | 140 +++++ crates/tests/noexcept/src/lib.rs | 25 + crates/tests/noexcept/src/test.idl | 19 + crates/tests/noexcept/tests/test.rs | 130 ++++ 15 files changed, 1142 insertions(+), 64 deletions(-) create mode 100644 crates/tests/noexcept/Cargo.toml create mode 100644 crates/tests/noexcept/build.rs create mode 100644 crates/tests/noexcept/src/bindings.rs create mode 100644 crates/tests/noexcept/src/interop.cpp create mode 100644 crates/tests/noexcept/src/lib.rs create mode 100644 crates/tests/noexcept/src/test.idl create mode 100644 crates/tests/noexcept/tests/test.rs diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 5936bbb2aa..8ee1f01732 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -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 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e24c3ec7f6..337bf8ca18 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -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 @@ -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 diff --git a/crates/libs/bindgen/src/metadata.rs b/crates/libs/bindgen/src/metadata.rs index a345d30ad3..af2c387dde 100644 --- a/crates/libs/bindgen/src/metadata.rs +++ b/crates/libs/bindgen/src/metadata.rs @@ -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") +} + pub fn type_is_borrowed(ty: &Type) -> bool { match ty { Type::TypeDef(row, _) => !type_def_is_blittable(*row), diff --git a/crates/libs/bindgen/src/rust/winrt_methods.rs b/crates/libs/bindgen/src/rust/winrt_methods.rs index 0ac752d43b..345cb01e75 100644 --- a/crates/libs/bindgen/src/rust/winrt_methods.rs +++ b/crates/libs/bindgen/src/rust/winrt_methods.rs @@ -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! { () }, @@ -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! {}, _ => { @@ -47,30 +60,60 @@ 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); + } + } 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); + 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__)) } } } }; @@ -78,7 +121,7 @@ pub fn writer( 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 @@ -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 @@ -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 }) } } @@ -189,10 +232,13 @@ 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 { @@ -200,20 +246,39 @@ pub fn gen_upcall( }; 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__); + 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() } } } @@ -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() } } } diff --git a/crates/libs/bindgen/src/rust/writer.rs b/crates/libs/bindgen/src/rust/writer.rs index e6e6189eee..c37778cd97 100644 --- a/crates/libs/bindgen/src/rust/writer.rs +++ b/crates/libs/bindgen/src/rust/writer.rs @@ -1284,31 +1284,44 @@ 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 { - metadata::Type::Void => quote! { () }, - _ => { - let tokens = self.type_name(&signature.return_type); + let return_type_tokens = if signature.return_type == metadata::Type::Void { + quote! { () } + } else { + let tokens = self.type_name(&signature.return_type); - if signature.return_type.is_winrt_array() { - quote! { windows_core::Array<#tokens> } - } else { - tokens - } + if signature.return_type.is_winrt_array() { + quote! { windows_core::Array<#tokens> } + } else { + tokens } }; + 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! {}; diff --git a/crates/libs/windows/src/Windows/UI/UIAutomation/Core/impl.rs b/crates/libs/windows/src/Windows/UI/UIAutomation/Core/impl.rs index 08dfd59472..ca97630d6f 100644 --- a/crates/libs/windows/src/Windows/UI/UIAutomation/Core/impl.rs +++ b/crates/libs/windows/src/Windows/UI/UIAutomation/Core/impl.rs @@ -1,5 +1,5 @@ pub trait ICoreAutomationConnectionBoundObjectProvider_Impl: Sized { - fn IsComThreadingRequired(&self) -> windows_core::Result; + fn IsComThreadingRequired(&self) -> bool; } impl windows_core::RuntimeName for ICoreAutomationConnectionBoundObjectProvider { const NAME: &'static str = "Windows.UI.UIAutomation.Core.ICoreAutomationConnectionBoundObjectProvider"; @@ -9,13 +9,9 @@ impl ICoreAutomationConnectionBoundObjectProvider_Vtbl { unsafe extern "system" fn IsComThreadingRequired, 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::(), diff --git a/crates/libs/windows/src/Windows/UI/UIAutomation/Core/mod.rs b/crates/libs/windows/src/Windows/UI/UIAutomation/Core/mod.rs index b7db3c05b0..39b1d59570 100644 --- a/crates/libs/windows/src/Windows/UI/UIAutomation/Core/mod.rs +++ b/crates/libs/windows/src/Windows/UI/UIAutomation/Core/mod.rs @@ -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 { + 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__ } } } diff --git a/crates/libs/windows/src/Windows/UI/UIAutomation/mod.rs b/crates/libs/windows/src/Windows/UI/UIAutomation/mod.rs index c2f3cef80f..6a980ffaaf 100644 --- a/crates/libs/windows/src/Windows/UI/UIAutomation/mod.rs +++ b/crates/libs/windows/src/Windows/UI/UIAutomation/mod.rs @@ -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 { + 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 { @@ -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 { + 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 { diff --git a/crates/tests/noexcept/Cargo.toml b/crates/tests/noexcept/Cargo.toml new file mode 100644 index 0000000000..881ecabdda --- /dev/null +++ b/crates/tests/noexcept/Cargo.toml @@ -0,0 +1,17 @@ +[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" + +[build-dependencies] +cc = "1.0" + +[build-dependencies.cppwinrt] +path = "../../libs/cppwinrt" diff --git a/crates/tests/noexcept/build.rs b/crates/tests/noexcept/build.rs new file mode 100644 index 0000000000..3cf9fbfe49 --- /dev/null +++ b/crates/tests/noexcept/build.rs @@ -0,0 +1,59 @@ +fn main() { + if !cfg!(target_env = "msvc") { + return; + } + + println!("cargo:rerun-if-changed=src/test.idl"); + let metadata_dir = format!("{}\\System32\\WinMetadata", env!("windir")); + let mut command = std::process::Command::new("midlrt.exe"); + println!("cargo:rerun-if-changed=src/interop.cpp"); + println!("cargo:rustc-link-lib=windows.0.52.0"); + + command.args([ + "/winrt", + "/nomidl", + "/h", + "nul", + "/metadata_dir", + &metadata_dir, + "/reference", + &format!("{metadata_dir}\\Windows.Foundation.winmd"), + "/winmd", + "test.winmd", + "src/test.idl", + ]); + + if !command.status().unwrap().success() { + panic!("Failed to run midlrt"); + } + + if let Err(error) = windows_bindgen::bindgen([ + "--in", + "test.winmd", + &metadata_dir, + "--out", + "src/bindings.rs", + "--filter", + "Test", + "--config", + "implement", + ]) { + panic!("{error}"); + } + + cppwinrt::cppwinrt([ + "-in", + "test.winmd", + &format!("{}\\System32\\WinMetadata", env!("windir")), + "-out", + "src", + ]) + .unwrap(); + + cc::Build::new() + .cpp(true) + .std("c++20") + .flag("/EHsc") + .file("src/interop.cpp") + .compile("interop"); +} diff --git a/crates/tests/noexcept/src/bindings.rs b/crates/tests/noexcept/src/bindings.rs new file mode 100644 index 0000000000..1f4a837e1a --- /dev/null +++ b/crates/tests/noexcept/src/bindings.rs @@ -0,0 +1,590 @@ +// Bindings generated by `windows-bindgen` 0.56.0 + +#![allow( + non_snake_case, + non_upper_case_globals, + non_camel_case_types, + dead_code, + clippy::all +)] +windows_core::imp::define_interface!(ITest, ITest_Vtbl, 0x37b05fc1_6ee1_5798_b48d_602875fb73a2); +impl core::ops::Deref for ITest { + type Target = windows_core::IInspectable; + fn deref(&self) -> &Self::Target { + unsafe { core::mem::transmute(self) } + } +} +windows_core::imp::interface_hierarchy!(ITest, windows_core::IUnknown, windows_core::IInspectable); +impl ITest { + pub fn MethodString(&self, test: &windows_core::HSTRING) -> windows_core::Result<()> { + let this = self; + unsafe { + (windows_core::Interface::vtable(this).MethodString)( + windows_core::Interface::as_raw(this), + core::mem::transmute_copy(test), + ) + .ok() + } + } + pub fn MethodInt32(&self, test: i32) -> windows_core::Result<()> { + let this = self; + unsafe { + (windows_core::Interface::vtable(this).MethodInt32)( + windows_core::Interface::as_raw(this), + test, + ) + .ok() + } + } + pub fn MethodTest(&self, test: P0) -> windows_core::Result<()> + where + P0: windows_core::Param, + { + let this = self; + unsafe { + (windows_core::Interface::vtable(this).MethodTest)( + windows_core::Interface::as_raw(this), + test.param().abi(), + ) + .ok() + } + } + pub fn String(&self) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).String)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn SetString(&self, value: &windows_core::HSTRING) -> windows_core::Result<()> { + let this = self; + unsafe { + (windows_core::Interface::vtable(this).SetString)( + windows_core::Interface::as_raw(this), + core::mem::transmute_copy(value), + ) + .ok() + } + } + pub fn Int32(&self) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Int32)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn SetInt32(&self, value: i32) -> windows_core::Result<()> { + let this = self; + unsafe { + (windows_core::Interface::vtable(this).SetInt32)( + windows_core::Interface::as_raw(this), + value, + ) + .ok() + } + } + pub fn Test(&self) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Test)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn SetTest(&self, value: P0) -> windows_core::Result<()> + where + P0: windows_core::Param, + { + let this = self; + unsafe { + (windows_core::Interface::vtable(this).SetTest)( + windows_core::Interface::as_raw(this), + value.param().abi(), + ) + .ok() + } + } + pub fn MethodStringN(&self, test: &windows_core::HSTRING) { + let this = self; + unsafe { + let hresult__ = (windows_core::Interface::vtable(this).MethodStringN)( + windows_core::Interface::as_raw(this), + core::mem::transmute_copy(test), + ); + debug_assert!(hresult__.0 == 0); + } + } + pub fn MethodInt32N(&self, test: i32) { + let this = self; + unsafe { + let hresult__ = (windows_core::Interface::vtable(this).MethodInt32N)( + windows_core::Interface::as_raw(this), + test, + ); + debug_assert!(hresult__.0 == 0); + } + } + pub fn MethodTestN(&self, test: P0) + where + P0: windows_core::Param, + { + let this = self; + unsafe { + let hresult__ = (windows_core::Interface::vtable(this).MethodTestN)( + windows_core::Interface::as_raw(this), + test.param().abi(), + ); + debug_assert!(hresult__.0 == 0); + } + } + pub fn StringN(&self) -> windows_core::HSTRING { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + let hresult__ = (windows_core::Interface::vtable(this).StringN)( + windows_core::Interface::as_raw(this), + &mut result__, + ); + debug_assert!(hresult__.0 == 0); + core::mem::transmute(result__) + } + } + pub fn SetStringN(&self, value: &windows_core::HSTRING) { + let this = self; + unsafe { + let hresult__ = (windows_core::Interface::vtable(this).SetStringN)( + windows_core::Interface::as_raw(this), + core::mem::transmute_copy(value), + ); + debug_assert!(hresult__.0 == 0); + } + } + pub fn Int32N(&self) -> i32 { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + let hresult__ = (windows_core::Interface::vtable(this).Int32N)( + windows_core::Interface::as_raw(this), + &mut result__, + ); + debug_assert!(hresult__.0 == 0); + result__ + } + } + pub fn SetInt32N(&self, value: i32) { + let this = self; + unsafe { + let hresult__ = (windows_core::Interface::vtable(this).SetInt32N)( + windows_core::Interface::as_raw(this), + value, + ); + debug_assert!(hresult__.0 == 0); + } + } + pub fn TestN(&self) -> Option { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + let hresult__ = (windows_core::Interface::vtable(this).TestN)( + windows_core::Interface::as_raw(this), + &mut result__, + ); + debug_assert!(hresult__.0 == 0); + core::mem::transmute(result__) + } + } + pub fn SetTestN(&self, value: P0) + where + P0: windows_core::Param, + { + let this = self; + unsafe { + let hresult__ = (windows_core::Interface::vtable(this).SetTestN)( + windows_core::Interface::as_raw(this), + value.param().abi(), + ); + debug_assert!(hresult__.0 == 0); + } + } +} +impl windows_core::RuntimeType for ITest { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +#[repr(C)] +pub struct ITest_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub MethodString: unsafe extern "system" fn( + *mut core::ffi::c_void, + core::mem::MaybeUninit, + ) -> windows_core::HRESULT, + pub MethodInt32: + unsafe extern "system" fn(*mut core::ffi::c_void, i32) -> windows_core::HRESULT, + pub MethodTest: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub String: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::mem::MaybeUninit, + ) -> windows_core::HRESULT, + pub SetString: unsafe extern "system" fn( + *mut core::ffi::c_void, + core::mem::MaybeUninit, + ) -> windows_core::HRESULT, + pub Int32: unsafe extern "system" fn(*mut core::ffi::c_void, *mut i32) -> windows_core::HRESULT, + pub SetInt32: unsafe extern "system" fn(*mut core::ffi::c_void, i32) -> windows_core::HRESULT, + pub Test: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub SetTest: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub MethodStringN: unsafe extern "system" fn( + *mut core::ffi::c_void, + core::mem::MaybeUninit, + ) -> windows_core::HRESULT, + pub MethodInt32N: + unsafe extern "system" fn(*mut core::ffi::c_void, i32) -> windows_core::HRESULT, + pub MethodTestN: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub StringN: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::mem::MaybeUninit, + ) -> windows_core::HRESULT, + pub SetStringN: unsafe extern "system" fn( + *mut core::ffi::c_void, + core::mem::MaybeUninit, + ) -> windows_core::HRESULT, + pub Int32N: + unsafe extern "system" fn(*mut core::ffi::c_void, *mut i32) -> windows_core::HRESULT, + pub SetInt32N: unsafe extern "system" fn(*mut core::ffi::c_void, i32) -> windows_core::HRESULT, + pub TestN: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub SetTestN: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + ) -> windows_core::HRESULT, +} +pub trait ITest_Impl: Sized { + fn MethodString(&self, test: &windows_core::HSTRING) -> windows_core::Result<()>; + fn MethodInt32(&self, test: i32) -> windows_core::Result<()>; + fn MethodTest(&self, test: Option<&ITest>) -> windows_core::Result<()>; + fn String(&self) -> windows_core::Result; + fn SetString(&self, value: &windows_core::HSTRING) -> windows_core::Result<()>; + fn Int32(&self) -> windows_core::Result; + fn SetInt32(&self, value: i32) -> windows_core::Result<()>; + fn Test(&self) -> windows_core::Result; + fn SetTest(&self, value: Option<&ITest>) -> windows_core::Result<()>; + fn MethodStringN(&self, test: &windows_core::HSTRING); + fn MethodInt32N(&self, test: i32); + fn MethodTestN(&self, test: Option<&ITest>); + fn StringN(&self) -> windows_core::HSTRING; + fn SetStringN(&self, value: &windows_core::HSTRING); + fn Int32N(&self) -> i32; + fn SetInt32N(&self, value: i32); + fn TestN(&self) -> Option; + fn SetTestN(&self, value: Option<&ITest>); +} +impl windows_core::RuntimeName for ITest { + const NAME: &'static str = "Test.ITest"; +} +impl ITest_Vtbl { + pub const fn new< + Identity: windows_core::IUnknownImpl, + Impl: ITest_Impl, + const OFFSET: isize, + >() -> ITest_Vtbl { + unsafe extern "system" fn MethodString< + Identity: windows_core::IUnknownImpl, + Impl: ITest_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + test: core::mem::MaybeUninit, + ) -> windows_core::HRESULT { + let this = (this as *const *const ()).offset(OFFSET) as *const Identity; + let this = (*this).get_impl(); + ITest_Impl::MethodString(this, core::mem::transmute(&test)).into() + } + unsafe extern "system" fn MethodInt32< + Identity: windows_core::IUnknownImpl, + Impl: ITest_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + test: i32, + ) -> windows_core::HRESULT { + let this = (this as *const *const ()).offset(OFFSET) as *const Identity; + let this = (*this).get_impl(); + ITest_Impl::MethodInt32(this, test).into() + } + unsafe extern "system" fn MethodTest< + Identity: windows_core::IUnknownImpl, + Impl: ITest_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + test: *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + let this = (this as *const *const ()).offset(OFFSET) as *const Identity; + let this = (*this).get_impl(); + ITest_Impl::MethodTest(this, windows_core::from_raw_borrowed(&test)).into() + } + unsafe extern "system" fn String< + Identity: windows_core::IUnknownImpl, + Impl: ITest_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut core::mem::MaybeUninit, + ) -> windows_core::HRESULT { + let this = (this as *const *const ()).offset(OFFSET) as *const Identity; + let this = (*this).get_impl(); + match ITest_Impl::String(this) { + Ok(ok__) => { + core::ptr::write(result__, core::mem::transmute_copy(&ok__)); + core::mem::forget(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + unsafe extern "system" fn SetString< + Identity: windows_core::IUnknownImpl, + Impl: ITest_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + value: core::mem::MaybeUninit, + ) -> windows_core::HRESULT { + let this = (this as *const *const ()).offset(OFFSET) as *const Identity; + let this = (*this).get_impl(); + ITest_Impl::SetString(this, core::mem::transmute(&value)).into() + } + unsafe extern "system" fn Int32< + Identity: windows_core::IUnknownImpl, + Impl: ITest_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut i32, + ) -> windows_core::HRESULT { + let this = (this as *const *const ()).offset(OFFSET) as *const Identity; + let this = (*this).get_impl(); + match ITest_Impl::Int32(this) { + Ok(ok__) => { + core::ptr::write(result__, core::mem::transmute_copy(&ok__)); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + unsafe extern "system" fn SetInt32< + Identity: windows_core::IUnknownImpl, + Impl: ITest_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + value: i32, + ) -> windows_core::HRESULT { + let this = (this as *const *const ()).offset(OFFSET) as *const Identity; + let this = (*this).get_impl(); + ITest_Impl::SetInt32(this, value).into() + } + unsafe extern "system" fn Test< + Identity: windows_core::IUnknownImpl, + Impl: ITest_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + let this = (this as *const *const ()).offset(OFFSET) as *const Identity; + let this = (*this).get_impl(); + match ITest_Impl::Test(this) { + Ok(ok__) => { + core::ptr::write(result__, core::mem::transmute_copy(&ok__)); + core::mem::forget(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + unsafe extern "system" fn SetTest< + Identity: windows_core::IUnknownImpl, + Impl: ITest_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + value: *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + let this = (this as *const *const ()).offset(OFFSET) as *const Identity; + let this = (*this).get_impl(); + ITest_Impl::SetTest(this, windows_core::from_raw_borrowed(&value)).into() + } + unsafe extern "system" fn MethodStringN< + Identity: windows_core::IUnknownImpl, + Impl: ITest_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + test: core::mem::MaybeUninit, + ) -> windows_core::HRESULT { + let this = (this as *const *const ()).offset(OFFSET) as *const Identity; + let this = (*this).get_impl(); + ITest_Impl::MethodStringN(this, core::mem::transmute(&test)); + windows_core::HRESULT(0) + } + unsafe extern "system" fn MethodInt32N< + Identity: windows_core::IUnknownImpl, + Impl: ITest_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + test: i32, + ) -> windows_core::HRESULT { + let this = (this as *const *const ()).offset(OFFSET) as *const Identity; + let this = (*this).get_impl(); + ITest_Impl::MethodInt32N(this, test); + windows_core::HRESULT(0) + } + unsafe extern "system" fn MethodTestN< + Identity: windows_core::IUnknownImpl, + Impl: ITest_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + test: *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + let this = (this as *const *const ()).offset(OFFSET) as *const Identity; + let this = (*this).get_impl(); + ITest_Impl::MethodTestN(this, windows_core::from_raw_borrowed(&test)); + windows_core::HRESULT(0) + } + unsafe extern "system" fn StringN< + Identity: windows_core::IUnknownImpl, + Impl: ITest_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut core::mem::MaybeUninit, + ) -> windows_core::HRESULT { + let this = (this as *const *const ()).offset(OFFSET) as *const Identity; + let this = (*this).get_impl(); + let ok__ = ITest_Impl::StringN(this); + core::ptr::write(result__, core::mem::transmute_copy(&ok__)); + core::mem::forget(ok__); + windows_core::HRESULT(0) + } + unsafe extern "system" fn SetStringN< + Identity: windows_core::IUnknownImpl, + Impl: ITest_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + value: core::mem::MaybeUninit, + ) -> windows_core::HRESULT { + let this = (this as *const *const ()).offset(OFFSET) as *const Identity; + let this = (*this).get_impl(); + ITest_Impl::SetStringN(this, core::mem::transmute(&value)); + windows_core::HRESULT(0) + } + unsafe extern "system" fn Int32N< + Identity: windows_core::IUnknownImpl, + Impl: ITest_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut i32, + ) -> windows_core::HRESULT { + let this = (this as *const *const ()).offset(OFFSET) as *const Identity; + let this = (*this).get_impl(); + let ok__ = ITest_Impl::Int32N(this); + core::ptr::write(result__, core::mem::transmute_copy(&ok__)); + windows_core::HRESULT(0) + } + unsafe extern "system" fn SetInt32N< + Identity: windows_core::IUnknownImpl, + Impl: ITest_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + value: i32, + ) -> windows_core::HRESULT { + let this = (this as *const *const ()).offset(OFFSET) as *const Identity; + let this = (*this).get_impl(); + ITest_Impl::SetInt32N(this, value); + windows_core::HRESULT(0) + } + unsafe extern "system" fn TestN< + Identity: windows_core::IUnknownImpl, + Impl: ITest_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + let this = (this as *const *const ()).offset(OFFSET) as *const Identity; + let this = (*this).get_impl(); + let ok__ = ITest_Impl::TestN(this); + core::ptr::write(result__, core::mem::transmute_copy(&ok__)); + core::mem::forget(ok__); + windows_core::HRESULT(0) + } + unsafe extern "system" fn SetTestN< + Identity: windows_core::IUnknownImpl, + Impl: ITest_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + value: *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + let this = (this as *const *const ()).offset(OFFSET) as *const Identity; + let this = (*this).get_impl(); + ITest_Impl::SetTestN(this, windows_core::from_raw_borrowed(&value)); + windows_core::HRESULT(0) + } + Self { + base__: windows_core::IInspectable_Vtbl::new::(), + MethodString: MethodString::, + MethodInt32: MethodInt32::, + MethodTest: MethodTest::, + String: String::, + SetString: SetString::, + Int32: Int32::, + SetInt32: SetInt32::, + Test: Test::, + SetTest: SetTest::, + MethodStringN: MethodStringN::, + MethodInt32N: MethodInt32N::, + MethodTestN: MethodTestN::, + StringN: StringN::, + SetStringN: SetStringN::, + Int32N: Int32N::, + SetInt32N: SetInt32N::, + TestN: TestN::, + SetTestN: SetTestN::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == &::IID + } +} diff --git a/crates/tests/noexcept/src/interop.cpp b/crates/tests/noexcept/src/interop.cpp new file mode 100644 index 0000000000..595ad741dd --- /dev/null +++ b/crates/tests/noexcept/src/interop.cpp @@ -0,0 +1,140 @@ +#include +#include "winrt/Test.h" + +using namespace winrt; +using namespace winrt::Test; + +void test_except(ITest const &test) +{ + test.MethodString(L"abc"); + assert(test.String() == L"abc"); + + test.MethodInt32(123); + assert(test.Int32() == 123); + + test.MethodTest(test); + assert(test.Test() == test); +} + +void test_noexcept(ITest const &test) +{ + test.MethodStringN(L"abc"); + assert(test.StringN() == L"abc"); + + test.MethodInt32N(123); + assert(test.Int32N() == 123); + + test.MethodTestN(test); + assert(test.TestN() == test); +} + +struct Implementation : implements +{ + hstring m_string; + int32_t m_int32; + ITest m_test; + + void MethodString(hstring const &value) + { + m_string = value; + } + void MethodInt32(int32_t value) + { + m_int32 = value; + } + void MethodTest(ITest const &value) + { + m_test = value; + } + hstring String() const + { + return m_string; + } + void String(hstring const &value) + { + m_string = value; + } + int32_t Int32() const + { + return m_int32; + } + void Int32(int32_t value) + { + m_int32 = value; + } + ITest Test() const + { + return m_test; + } + void Test(ITest const &value) + { + m_test = value; + } + + void MethodStringN(hstring const &value) noexcept + { + m_string = value; + } + void MethodInt32N(int32_t value) noexcept + { + m_int32 = value; + } + void MethodTestN(ITest const &value) noexcept + { + m_test = value; + } + hstring StringN() const noexcept + { + return m_string; + } + void StringN(hstring const &value) noexcept + { + m_string = value; + } + int32_t Int32N() const noexcept + { + return m_int32; + } + void Int32N(int32_t value) noexcept + { + m_int32 = value; + } + ITest TestN() const noexcept + { + return m_test; + } + void TestN(ITest const &value) noexcept + { + m_test = value; + } +}; + +extern "C" +{ + HRESULT __stdcall consume(void *abi) noexcept + try + { + ITest const &test = *reinterpret_cast(&abi); + + test_noexcept(test); + test_except(test); + + return S_OK; + } + catch (...) + { + return to_hresult(); + } + + HRESULT __stdcall produce(void **abi) noexcept + try + { + *abi = detach_abi(make()); + + return S_OK; + } + catch (...) + { + return to_hresult(); + } +} diff --git a/crates/tests/noexcept/src/lib.rs b/crates/tests/noexcept/src/lib.rs new file mode 100644 index 0000000000..6bff33433c --- /dev/null +++ b/crates/tests/noexcept/src/lib.rs @@ -0,0 +1,25 @@ +#![cfg(target_env = "msvc")] + +mod bindings; +pub use bindings::*; +pub use windows_core::*; + +pub fn consume(test: &ITest) -> Result<()> { + extern "system" { + fn consume(test: Ref) -> HRESULT; + } + + unsafe { consume(std::mem::transmute_copy(test)).ok() } +} + +pub fn produce() -> Result { + extern "system" { + fn produce(test: *mut *mut std::ffi::c_void) -> HRESULT; + } + + unsafe { + let mut test = None; + produce(&mut test as *mut _ as *mut _).ok()?; + Type::from_default(&test) + } +} diff --git a/crates/tests/noexcept/src/test.idl b/crates/tests/noexcept/src/test.idl new file mode 100644 index 0000000000..721162acd2 --- /dev/null +++ b/crates/tests/noexcept/src/test.idl @@ -0,0 +1,19 @@ +namespace Test +{ + interface ITest + { + void MethodString(String test); + void MethodInt32(Int32 test); + void MethodTest(ITest test); + String String; + Int32 Int32; + ITest Test; + + [noexcept] void MethodStringN(String test); + [noexcept] void MethodInt32N(Int32 test); + [noexcept] void MethodTestN(ITest test); + [noexcept] String StringN; + [noexcept] Int32 Int32N; + [noexcept] ITest TestN; + } +} diff --git a/crates/tests/noexcept/tests/test.rs b/crates/tests/noexcept/tests/test.rs new file mode 100644 index 0000000000..0ab220686a --- /dev/null +++ b/crates/tests/noexcept/tests/test.rs @@ -0,0 +1,130 @@ +#![cfg(target_env = "msvc")] + +use test_noexcept::*; + +#[implement(ITest)] +#[derive(Default)] +struct Test(std::sync::RwLock<(HSTRING, i32, Option)>); + +impl ITest_Impl for Test { + fn MethodString(&self, test: &HSTRING) -> Result<()> { + let mut this = self.0.write().unwrap(); + this.0 = test.clone(); + Ok(()) + } + fn MethodInt32(&self, test: i32) -> Result<()> { + let mut this = self.0.write().unwrap(); + this.1 = test; + Ok(()) + } + fn MethodTest(&self, test: Option<&ITest>) -> Result<()> { + let mut this = self.0.write().unwrap(); + this.2 = test.cloned(); + Ok(()) + } + fn String(&self) -> Result { + let this = self.0.read().unwrap(); + Ok(this.0.clone()) + } + fn SetString(&self, value: &HSTRING) -> Result<()> { + let mut this = self.0.write().unwrap(); + this.0 = value.clone(); + Ok(()) + } + fn Int32(&self) -> Result { + let this = self.0.read().unwrap(); + Ok(this.1) + } + fn SetInt32(&self, value: i32) -> Result<()> { + let mut this = self.0.write().unwrap(); + this.1 = value; + Ok(()) + } + fn Test(&self) -> Result { + let this = self.0.read().unwrap(); + this.2.clone().ok_or_else(Error::empty) + } + fn SetTest(&self, value: Option<&ITest>) -> Result<()> { + let mut this = self.0.write().unwrap(); + this.2 = value.cloned(); + Ok(()) + } + + fn MethodStringN(&self, test: &HSTRING) { + let mut this = self.0.write().unwrap(); + this.0 = test.clone(); + } + fn MethodInt32N(&self, test: i32) { + let mut this = self.0.write().unwrap(); + this.1 = test; + } + fn MethodTestN(&self, test: Option<&ITest>) { + let mut this = self.0.write().unwrap(); + this.2 = test.cloned(); + } + fn StringN(&self) -> HSTRING { + let this = self.0.read().unwrap(); + this.0.clone() + } + fn SetStringN(&self, value: &HSTRING) { + let mut this = self.0.write().unwrap(); + this.0 = value.clone(); + } + fn Int32N(&self) -> i32 { + let this = self.0.read().unwrap(); + this.1 + } + fn SetInt32N(&self, value: i32) { + let mut this = self.0.write().unwrap(); + this.1 = value; + } + fn TestN(&self) -> Option { + let this = self.0.read().unwrap(); + this.2.clone() + } + fn SetTestN(&self, value: Option<&ITest>) { + let mut this = self.0.write().unwrap(); + this.2 = value.cloned(); + } +} + +fn test_except(test: &ITest) -> Result<()> { + test.MethodString(h!("abc"))?; + assert_eq!(test.String()?, "abc"); + + test.MethodInt32(123)?; + assert_eq!(test.Int32()?, 123); + + test.MethodTest(test)?; + assert_eq!(&test.Test()?, test); + + Ok(()) +} + +fn test_noexcept(test: &ITest) { + test.MethodStringN(h!("abc")); + assert_eq!(test.StringN(), "abc"); + + test.MethodInt32N(123); + assert_eq!(test.Int32N(), 123); + + test.MethodTestN(test); + assert_eq!(test.TestN().as_ref(), Some(test)); +} + +#[test] +fn test_rust() -> Result<()> { + let test: ITest = Test::default().into(); + test_noexcept(&test); + test_except(&test) +} + +#[test] +fn test_cpp() -> Result<()> { + let test: ITest = Test::default().into(); + consume(&test)?; + + let test: ITest = produce()?; + test_noexcept(&test); + test_except(&test) +}