From 700b279b57a7731778025b56fd6190493b09f305 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 24 Jun 2022 22:19:15 +0200 Subject: [PATCH 1/3] Make calling `throw` no longer UB by using "C-unwind" --- .github/workflows/ci.yml | 2 +- objc-sys/CHANGELOG.md | 3 +++ objc-sys/Cargo.toml | 3 +++ objc-sys/src/class.rs | 26 +++++++++++-------- objc-sys/src/exception.rs | 26 ++++++++++++------- objc-sys/src/lib.rs | 44 +++++++++++++++++++++++++++----- objc-sys/src/message.rs | 7 ++++- objc-sys/src/protocol.rs | 2 ++ objc-sys/src/rc.rs | 4 ++- objc-sys/src/various.rs | 16 +++++++++--- objc2-encode/Cargo.toml | 3 +++ objc2-encode/src/encode.rs | 26 +++++++++++++------ objc2-encode/src/lib.rs | 1 + objc2-foundation/src/array.rs | 1 + objc2-foundation/src/geometry.rs | 1 + objc2/CHANGELOG.md | 1 + objc2/Cargo.toml | 3 +++ objc2/src/declare.rs | 25 ++++++++++-------- objc2/src/exception.rs | 44 +++++++++++++++++++++++++------- objc2/src/lib.rs | 1 + objc2/src/message/mod.rs | 5 ++++ objc2/src/runtime.rs | 7 ++++- 22 files changed, 191 insertions(+), 60 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 101a9c85d..d4c9fcae8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -154,7 +154,7 @@ jobs: # Use --no-fail-fast, except with dinghy TESTARGS: ${{ matrix.dinghy && ' ' || '--no-fail-fast' }} ${{ matrix.test-args }} FEATURES: ${{ matrix.features || 'malloc,block,exception,catch_all,verify_message' }} - UNSTABLE_FEATURES: ${{ matrix.unstable-features || 'unstable-autoreleasesafe' }} + UNSTABLE_FEATURES: ${{ matrix.unstable-features || 'unstable-autoreleasesafe,unstable-c-unwind' }} runs-on: ${{ matrix.os }} diff --git a/objc-sys/CHANGELOG.md b/objc-sys/CHANGELOG.md index 42e2a36bc..ea3b8beb6 100644 --- a/objc-sys/CHANGELOG.md +++ b/objc-sys/CHANGELOG.md @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Unreleased - YYYY-MM-DD +### Added +* Added `unstable-c-unwind` feature. + ## 0.2.0-beta.0 - 2022-06-13 diff --git a/objc-sys/Cargo.toml b/objc-sys/Cargo.toml index 087d1e669..23fff9f3f 100644 --- a/objc-sys/Cargo.toml +++ b/objc-sys/Cargo.toml @@ -49,6 +49,9 @@ unstable-winobjc = ["gnustep-1-8"] # Link to ObjFW unstable-objfw = [] +# Use nightly c_unwind feature +unstable-c-unwind = [] + # Private unstable-exception = ["cc"] unstable-docsrs = [] diff --git a/objc-sys/src/class.rs b/objc-sys/src/class.rs index 5de95ed60..e85aca55b 100644 --- a/objc-sys/src/class.rs +++ b/objc-sys/src/class.rs @@ -23,6 +23,21 @@ pub struct objc_class { /// difference. type ivar_layout_type = u8; +// May call `resolveClassMethod:` or `resolveInstanceMethod:`. +extern_c_unwind! { + #[cfg(not(objfw))] + pub fn class_getClassMethod( + cls: *const objc_class, + name: *const objc_selector, + ) -> *const objc_method; + #[cfg(not(objfw))] // Available in newer versions + pub fn class_getInstanceMethod( + cls: *const objc_class, + name: *const objc_selector, + ) -> *const objc_method; +} + +// TODO: Hooks registered with objc_setHook_getClass may be allowed to unwind? extern_c! { pub fn objc_getClass(name: *const c_char) -> *const objc_class; pub fn objc_getRequiredClass(name: *const c_char) -> *const objc_class; @@ -97,19 +112,9 @@ extern_c! { #[cfg(not(objfw))] pub fn class_createInstance(cls: *const objc_class, extra_bytes: usize) -> *mut objc_object; #[cfg(not(objfw))] - pub fn class_getClassMethod( - cls: *const objc_class, - name: *const objc_selector, - ) -> *const objc_method; - #[cfg(not(objfw))] pub fn class_getClassVariable(cls: *const objc_class, name: *const c_char) -> *const objc_ivar; #[cfg(apple)] pub fn class_getImageName(cls: *const objc_class) -> *const c_char; - #[cfg(not(objfw))] // Available in newer versions - pub fn class_getInstanceMethod( - cls: *const objc_class, - name: *const objc_selector, - ) -> *const objc_method; pub fn class_getInstanceSize(cls: *const objc_class) -> usize; #[cfg(not(objfw))] pub fn class_getInstanceVariable( @@ -140,6 +145,7 @@ extern_c! { attributes: *const objc_property_attribute_t, attributes_len: c_uint, ); + // TODO: Verify unwinding pub fn class_respondsToSelector(cls: *const objc_class, sel: *const objc_selector) -> BOOL; #[cfg(not(objfw))] pub fn class_setIvarLayout(cls: *mut objc_class, layout: *const ivar_layout_type); diff --git a/objc-sys/src/exception.rs b/objc-sys/src/exception.rs index 86ad3909c..a4ca08d28 100644 --- a/objc-sys/src/exception.rs +++ b/objc-sys/src/exception.rs @@ -38,11 +38,12 @@ pub type objc_uncaught_exception_handler = pub type objc_exception_handler = unsafe extern "C" fn(unused: *mut objc_object, context: *mut c_void); -extern_c! { - #[cfg(any(gnustep, apple_new))] - pub fn objc_begin_catch(exc_buf: *mut c_void) -> *mut objc_object; - #[cfg(any(gnustep, apple_new))] - pub fn objc_end_catch(); +#[cfg(all(feature = "unstable-exception", not(feature = "unstable-c-unwind")))] +type TryCatchClosure = extern "C" fn(*mut c_void); +#[cfg(all(feature = "unstable-exception", feature = "unstable-c-unwind"))] +type TryCatchClosure = extern "C-unwind" fn(*mut c_void); + +extern_c_unwind! { /// See [`objc-exception.h`]. /// /// [`objc-exception.h`]: https://github.com/apple-oss-distributions/objc4/blob/objc4-818.2/runtime/objc-exception.h @@ -50,6 +51,16 @@ extern_c! { #[cfg(apple_new)] pub fn objc_exception_rethrow() -> !; + #[cfg(gnustep)] + pub fn objc_exception_rethrow(exc_buf: *mut c_void) -> !; +} + +extern_c! { + #[cfg(any(gnustep, apple_new))] + pub fn objc_begin_catch(exc_buf: *mut c_void) -> *mut objc_object; + #[cfg(any(gnustep, apple_new))] + pub fn objc_end_catch(); + #[cfg(apple_old)] pub fn objc_exception_try_enter(exception_data: *const c_void); @@ -61,9 +72,6 @@ extern_c! { // objc_exception_get_functions // objc_exception_set_functions - #[cfg(gnustep)] - pub fn objc_exception_rethrow(exc_buf: *mut c_void) -> !; - #[cfg(apple_new)] pub fn objc_setExceptionMatcher(f: objc_exception_matcher) -> objc_exception_matcher; #[cfg(apple_new)] @@ -105,7 +113,7 @@ extern_c! { /// [manual-asm]: https://gitlab.com/objrs/objrs/-/blob/b4f6598696b3fa622e6fddce7aff281770b0a8c2/src/exception.rs #[cfg(feature = "unstable-exception")] pub fn rust_objc_sys_0_2_try_catch_exception( - f: extern "C" fn(*mut c_void), + f: TryCatchClosure, context: *mut c_void, error: *mut *mut objc_object, ) -> c_uchar; diff --git a/objc-sys/src/lib.rs b/objc-sys/src/lib.rs index 5188cf016..2ad87df62 100644 --- a/objc-sys/src/lib.rs +++ b/objc-sys/src/lib.rs @@ -25,9 +25,7 @@ #![allow(non_upper_case_globals)] #![allow(non_snake_case)] #![doc(html_root_url = "https://docs.rs/objc-sys/0.2.0-beta.0")] - -// TODO: Replace `extern "C"` with `extern "C-unwind"` where applicable. -// See https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html. +#![cfg_attr(feature = "unstable-c-unwind", feature(c_unwind))] // TODO: Remove this and add "no-std" category to Cargo.toml // Requires a better solution for C-types in `no_std` crates. @@ -51,6 +49,7 @@ macro_rules! generate_linking_tests { $(#[$m:meta])* $v:vis fn $name:ident($($a:ident: $t:ty),* $(,)?) $(-> $r:ty)?; )+} + mod $test_name:ident; } => { $(#[$extern_m])* extern $abi {$( @@ -61,10 +60,9 @@ macro_rules! generate_linking_tests { $(#[$extern_m])* #[allow(deprecated)] #[cfg(test)] - mod test_linkable { + mod $test_name { #[allow(unused)] use super::*; - use std::println; $( $(#[$m])* @@ -74,7 +72,7 @@ macro_rules! generate_linking_tests { // symbol to be available. let f: unsafe extern $abi fn($($t),*) $(-> $r)? = crate::$name; // Execute side-effect to ensure it is not optimized away. - println!("{:p}", f); + std::println!("{:p}", f); } )+ } @@ -95,6 +93,40 @@ macro_rules! extern_c { $(#[$m])* $v fn $name($($a: $t),*) $(-> $r)?; )+} + mod test_linkable; + } + }; +} + +// A lot of places may call `+initialize`, but the runtime guards those calls +// with `@try/@catch` blocks already, so we don't need to mark every function +// "C-unwind", only certain ones! +macro_rules! extern_c_unwind { + { + $(#![$extern_m:meta])* + $( + $(#[$m:meta])* + $v:vis fn $name:ident($($a:ident: $t:ty),* $(,)?) $(-> $r:ty)?; + )+ + } => { + #[cfg(not(feature = "unstable-c-unwind"))] + generate_linking_tests! { + $(#[$extern_m])* + extern "C" {$( + $(#[$m])* + $v fn $name($($a: $t),*) $(-> $r)?; + )+} + mod test_linkable_unwind; + } + + #[cfg(feature = "unstable-c-unwind")] + generate_linking_tests! { + $(#[$extern_m])* + extern "C-unwind" {$( + $(#[$m])* + $v fn $name($($a: $t),*) $(-> $r)?; + )+} + mod test_linkable_unwind; } }; } diff --git a/objc-sys/src/message.rs b/objc-sys/src/message.rs index 8de6f1587..590fcc295 100644 --- a/objc-sys/src/message.rs +++ b/objc-sys/src/message.rs @@ -20,7 +20,12 @@ pub struct objc_super { pub super_class: *const objc_class, } -extern_c! { +// All message sending functions should use "C-unwind"! +// +// Note that lookup functions won't throw exceptions themselves, but they can +// call hooks, `resolveClassMethod:` and `resolveInstanceMethod:`, so we have +// to make those "C-unwind" as well! +extern_c_unwind! { #[cfg(any(gnustep, objfw))] pub fn objc_msg_lookup(receiver: *mut objc_object, sel: *const objc_selector) -> IMP; #[cfg(objfw)] diff --git a/objc-sys/src/protocol.rs b/objc-sys/src/protocol.rs index b8684ba0d..54c56d9e4 100644 --- a/objc-sys/src/protocol.rs +++ b/objc-sys/src/protocol.rs @@ -30,10 +30,12 @@ extern_c! { #[cfg(not(objfw))] pub fn objc_registerProtocol(proto: *mut objc_protocol); + // TODO: Verify unwinding pub fn protocol_conformsToProtocol( proto: *const objc_protocol, other: *const objc_protocol, ) -> BOOL; + // TODO: Verify unwinding pub fn protocol_isEqual(proto: *const objc_protocol, other: *const objc_protocol) -> BOOL; pub fn protocol_getName(proto: *const objc_protocol) -> *const c_char; diff --git a/objc-sys/src/rc.rs b/objc-sys/src/rc.rs index 91b209de7..f0ef03daf 100644 --- a/objc-sys/src/rc.rs +++ b/objc-sys/src/rc.rs @@ -14,7 +14,9 @@ use core::ffi::c_void; use crate::objc_object; -extern_c! { +// All of these very rarely unwind, but may if the user defined methods +// `retain`, `release`, `autorelease` or `dealloc` do. +extern_c_unwind! { // Autoreleasepool // ObjFW: Defined in `autorelease.h`, not available with libobjfw-rt! diff --git a/objc-sys/src/various.rs b/objc-sys/src/various.rs index 471394962..2334cb5bf 100644 --- a/objc-sys/src/various.rs +++ b/objc-sys/src/various.rs @@ -16,10 +16,15 @@ pub struct objc_ivar { _p: OpaqueData, } +#[cfg(not(feature = "unstable-c-unwind"))] +type InnerImp = unsafe extern "C" fn(); +#[cfg(feature = "unstable-c-unwind")] +type InnerImp = unsafe extern "C-unwind" fn(); + /// A nullable pointer to the start of a method implementation. /// /// Not all APIs are guaranteed to take NULL values; read the docs! -pub type IMP = Option; +pub type IMP = Option; // /// Not available on macOS x86. // /// @@ -35,6 +40,12 @@ pub type IMP = Option; // pub type objc_hook_lazyClassNamer = // unsafe extern "C" fn(cls: *const crate::objc_class) -> *const c_char; +extern_c_unwind! { + // Instead of being able to change this, it's a weak symbol on GNUStep. + #[cfg(any(apple, objfw))] + pub fn objc_enumerationMutation(obj: *mut objc_object); +} + extern_c! { #[cfg(not(objfw))] pub fn imp_getBlock(imp: IMP) -> *mut objc_object; @@ -58,9 +69,6 @@ extern_c! { #[cfg(apple)] pub fn objc_copyImageNames(out_len: *mut c_uint) -> *mut *const c_char; - // Instead of being able to change this, it's a weak symbol on GNUStep. - #[cfg(any(apple, objfw))] - pub fn objc_enumerationMutation(obj: *mut objc_object); #[cfg(any(apple, objfw))] pub fn objc_setEnumerationMutationHandler( handler: Option, diff --git a/objc2-encode/Cargo.toml b/objc2-encode/Cargo.toml index d2ceef554..6b6236475 100644 --- a/objc2-encode/Cargo.toml +++ b/objc2-encode/Cargo.toml @@ -26,6 +26,9 @@ default = ["std"] std = ["alloc"] alloc = [] +# Enables support for the nightly c_unwind feature +unstable-c-unwind = [] + [package.metadata.docs.rs] default-target = "x86_64-apple-darwin" diff --git a/objc2-encode/src/encode.rs b/objc2-encode/src/encode.rs index f9bdf009a..5e9ec4470 100644 --- a/objc2-encode/src/encode.rs +++ b/objc2-encode/src/encode.rs @@ -455,18 +455,23 @@ macro_rules! encode_fn_pointer_impl { const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING); } }; - ($($Arg: ident),+) => { + (# $abi:literal; $($Arg: ident),+) => { // Normal functions - encode_fn_pointer_impl!(@ extern "C" fn($($Arg),+) -> Ret, $($Arg),+ ); - encode_fn_pointer_impl!(@ unsafe extern "C" fn($($Arg),+) -> Ret, $($Arg),+ ); + encode_fn_pointer_impl!(@ extern $abi fn($($Arg),+) -> Ret, $($Arg),+ ); + encode_fn_pointer_impl!(@ unsafe extern $abi fn($($Arg),+) -> Ret, $($Arg),+ ); // Variadic functions - encode_fn_pointer_impl!(@ extern "C" fn($($Arg),+ , ...) -> Ret, $($Arg),+ ); - encode_fn_pointer_impl!(@ unsafe extern "C" fn($($Arg),+ , ...) -> Ret, $($Arg),+ ); + encode_fn_pointer_impl!(@ extern $abi fn($($Arg),+ , ...) -> Ret, $($Arg),+ ); + encode_fn_pointer_impl!(@ unsafe extern $abi fn($($Arg),+ , ...) -> Ret, $($Arg),+ ); }; - () => { + (# $abi:literal; ) => { // No variadic functions with 0 parameters - encode_fn_pointer_impl!(@ extern "C" fn() -> Ret, ); - encode_fn_pointer_impl!(@ unsafe extern "C" fn() -> Ret, ); + encode_fn_pointer_impl!(@ extern $abi fn() -> Ret, ); + encode_fn_pointer_impl!(@ unsafe extern $abi fn() -> Ret, ); + }; + ($($Arg: ident),*) => { + encode_fn_pointer_impl!(# "C"; $($Arg),*); + #[cfg(feature = "unstable-c-unwind")] + encode_fn_pointer_impl!(# "C-unwind"; $($Arg),*); }; } @@ -607,6 +612,11 @@ mod tests { >::ENCODING, Encoding::Pointer(&Encoding::Unknown) ); + #[cfg(feature = "unstable-c-unwind")] + assert_eq!( + ::ENCODING, + Encoding::Pointer(&Encoding::Unknown) + ); } #[test] diff --git a/objc2-encode/src/lib.rs b/objc2-encode/src/lib.rs index 2208b2953..ceac1cb42 100644 --- a/objc2-encode/src/lib.rs +++ b/objc2-encode/src/lib.rs @@ -94,6 +94,7 @@ #![warn(clippy::ptr_as_ptr)] // Update in Cargo.toml as well. #![doc(html_root_url = "https://docs.rs/objc2-encode/2.0.0-pre.0")] +#![cfg_attr(feature = "unstable-c-unwind", feature(c_unwind))] #[cfg(doctest)] #[doc = include_str!("../README.md")] diff --git a/objc2-foundation/src/array.rs b/objc2-foundation/src/array.rs index 42aaa287a..486652ac3 100644 --- a/objc2-foundation/src/array.rs +++ b/objc2-foundation/src/array.rs @@ -302,6 +302,7 @@ impl NSMutableArray { #[doc(alias = "sortUsingFunction:context:")] pub fn sort_by Ordering>(&mut self, compare: F) { + // TODO: "C-unwind" extern "C" fn compare_with_closure Ordering>( obj1: &U, obj2: &U, diff --git a/objc2-foundation/src/geometry.rs b/objc2-foundation/src/geometry.rs index c271f5000..ce2d7120a 100644 --- a/objc2-foundation/src/geometry.rs +++ b/objc2-foundation/src/geometry.rs @@ -315,6 +315,7 @@ mod tests { fn test_partial_eq() { use objc2::runtime::Bool; + // Note: No need to use "C-unwind" extern "C" { fn NSEqualPoints(a: NSPoint, b: NSPoint) -> Bool; fn NSEqualSizes(a: NSSize, b: NSSize) -> Bool; diff --git a/objc2/CHANGELOG.md b/objc2/CHANGELOG.md index 3394a7e59..992fff16d 100644 --- a/objc2/CHANGELOG.md +++ b/objc2/CHANGELOG.md @@ -28,6 +28,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Added the `"unstable-static-sel"` and `"unstable-static-sel-inlined"` feature flags to make the `sel!` macro (and by extension, the `msg_send!` macros) faster. +* Added `"unstable-c-unwind"` feature. ### Changed * **BREAKING:** `Sel` is now required to be non-null, which means that you diff --git a/objc2/Cargo.toml b/objc2/Cargo.toml index e0c527dff..bb03c89e2 100644 --- a/objc2/Cargo.toml +++ b/objc2/Cargo.toml @@ -55,6 +55,9 @@ unstable-static-sel-inlined = ["unstable-static-sel"] # Uses nightly features to make AutoreleasePool zero-cost even in debug mode unstable-autoreleasesafe = [] +# Uses the nightly c_unwind feature to make throwing safe +unstable-c-unwind = ["objc-sys/unstable-c-unwind"] + # Runtime selection. See `objc-sys` for details. apple = ["objc-sys/apple"] gnustep-1-7 = ["objc-sys/gnustep-1-7"] diff --git a/objc2/src/declare.rs b/objc2/src/declare.rs index 77b282a91..42c32a4ab 100644 --- a/objc2/src/declare.rs +++ b/objc2/src/declare.rs @@ -110,17 +110,22 @@ macro_rules! method_decl_impl { } } }; + (# $abi:literal; $($t:ident),*) => { + method_decl_impl!(-T, R, extern $abi fn(&T, Sel $(, $t)*) -> R, $($t),*); + method_decl_impl!(-T, R, extern $abi fn(&mut T, Sel $(, $t)*) -> R, $($t),*); + method_decl_impl!(-T, R, unsafe extern $abi fn(*const T, Sel $(, $t)*) -> R, $($t),*); + method_decl_impl!(-T, R, unsafe extern $abi fn(*mut T, Sel $(, $t)*) -> R, $($t),*); + method_decl_impl!(-T, R, unsafe extern $abi fn(&T, Sel $(, $t)*) -> R, $($t),*); + method_decl_impl!(-T, R, unsafe extern $abi fn(&mut T, Sel $(, $t)*) -> R, $($t),*); + + method_decl_impl!(@Class, R, extern $abi fn(&Class, Sel $(, $t)*) -> R, $($t),*); + method_decl_impl!(@Class, R, unsafe extern $abi fn(*const Class, Sel $(, $t)*) -> R, $($t),*); + method_decl_impl!(@Class, R, unsafe extern $abi fn(&Class, Sel $(, $t)*) -> R, $($t),*); + }; ($($t:ident),*) => { - method_decl_impl!(-T, R, extern "C" fn(&T, Sel $(, $t)*) -> R, $($t),*); - method_decl_impl!(-T, R, extern "C" fn(&mut T, Sel $(, $t)*) -> R, $($t),*); - method_decl_impl!(-T, R, unsafe extern "C" fn(*const T, Sel $(, $t)*) -> R, $($t),*); - method_decl_impl!(-T, R, unsafe extern "C" fn(*mut T, Sel $(, $t)*) -> R, $($t),*); - method_decl_impl!(-T, R, unsafe extern "C" fn(&T, Sel $(, $t)*) -> R, $($t),*); - method_decl_impl!(-T, R, unsafe extern "C" fn(&mut T, Sel $(, $t)*) -> R, $($t),*); - - method_decl_impl!(@Class, R, extern "C" fn(&Class, Sel $(, $t)*) -> R, $($t),*); - method_decl_impl!(@Class, R, unsafe extern "C" fn(*const Class, Sel $(, $t)*) -> R, $($t),*); - method_decl_impl!(@Class, R, unsafe extern "C" fn(&Class, Sel $(, $t)*) -> R, $($t),*); + method_decl_impl!(# "C"; $($t),*); + #[cfg(feature = "unstable-c-unwind")] + method_decl_impl!(# "C-unwind"; $($t),*); }; } diff --git a/objc2/src/exception.rs b/objc2/src/exception.rs index 2faa1c660..f8ddacf8e 100644 --- a/objc2/src/exception.rs +++ b/objc2/src/exception.rs @@ -36,7 +36,8 @@ use crate::runtime::Object; /// [`catch_unwind`]). /// /// This also invokes undefined behaviour until `C-unwind` is stabilized, see -/// [RFC-2945]. +/// [RFC-2945] - you can try this out on nightly using the `unstable-c-unwind` +/// feature flag. /// /// [`catch_unwind`]: std::panic::catch_unwind /// [RFC-2945]: https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html @@ -50,14 +51,38 @@ pub unsafe fn throw(exception: Option<&Id>) -> ! { } unsafe fn try_no_ret(closure: F) -> Result<(), Option>> { - extern "C" fn try_objc_execute_closure(closure: &mut Option) { - // This is always passed Some, so it's safe to unwrap - let closure = closure.take().unwrap(); - closure(); - } + #[cfg(not(feature = "unstable-c-unwind"))] + let f = { + extern "C" fn try_objc_execute_closure(closure: &mut Option) + where + F: FnOnce(), + { + // This is always passed Some, so it's safe to unwrap + let closure = closure.take().unwrap(); + closure(); + } + + let f: extern "C" fn(&mut Option) = try_objc_execute_closure; + let f: extern "C" fn(*mut c_void) = unsafe { mem::transmute(f) }; + f + }; + + #[cfg(feature = "unstable-c-unwind")] + let f = { + extern "C-unwind" fn try_objc_execute_closure(closure: &mut Option) + where + F: FnOnce(), + { + // This is always passed Some, so it's safe to unwrap + let closure = closure.take().unwrap(); + closure(); + } + + let f: extern "C-unwind" fn(&mut Option) = try_objc_execute_closure; + let f: extern "C-unwind" fn(*mut c_void) = unsafe { mem::transmute(f) }; + f + }; - let f: extern "C" fn(&mut Option) = try_objc_execute_closure; - let f: extern "C" fn(*mut c_void) = unsafe { mem::transmute(f) }; // Wrap the closure in an Option so it can be taken let mut closure = Some(closure); let context: *mut Option = &mut closure; @@ -94,7 +119,8 @@ unsafe fn try_no_ret(closure: F) -> Result<(), Option(closure: impl FnOnce() -> R) -> Result>> { diff --git a/objc2/src/lib.rs b/objc2/src/lib.rs index 054c42257..1fa1b00c4 100644 --- a/objc2/src/lib.rs +++ b/objc2/src/lib.rs @@ -168,6 +168,7 @@ feature = "unstable-autoreleasesafe", feature(negative_impls, auto_traits) )] +#![cfg_attr(feature = "unstable-c-unwind", feature(c_unwind))] #![warn(elided_lifetimes_in_paths)] #![warn(missing_docs)] #![deny(non_ascii_idents)] diff --git a/objc2/src/message/mod.rs b/objc2/src/message/mod.rs index e8a4f92b8..9d6468687 100644 --- a/objc2/src/message/mod.rs +++ b/objc2/src/message/mod.rs @@ -320,9 +320,14 @@ macro_rules! message_args_impl { // type before being called; the msgSend functions are not // parametric, but instead "trampolines" to the actual // method implementations. + #[cfg(not(feature = "unstable-c-unwind"))] let imp: unsafe extern "C" fn(*mut Object, Sel $(, $t)*) -> R = unsafe { mem::transmute(imp) }; + #[cfg(feature = "unstable-c-unwind")] + let imp: unsafe extern "C-unwind" fn(*mut Object, Sel $(, $t)*) -> R = unsafe { + mem::transmute(imp) + }; // TODO: On x86_64 it would be more efficient to use a GOT // entry here (e.g. adding `nonlazybind` in LLVM). // Same can be said of e.g. `objc_retain` and `objc_release`. diff --git a/objc2/src/runtime.rs b/objc2/src/runtime.rs index 2c2ea44ec..0a8dd4530 100644 --- a/objc2/src/runtime.rs +++ b/objc2/src/runtime.rs @@ -103,13 +103,18 @@ standard_pointer_impls!(Ivar, Method, Class); #[repr(C)] pub struct Object(ffi::objc_object); +#[cfg(not(feature = "unstable-c-unwind"))] +type InnerImp = unsafe extern "C" fn(); +#[cfg(feature = "unstable-c-unwind")] +type InnerImp = unsafe extern "C-unwind" fn(); + /// A pointer to the start of a method implementation. /// /// # Safety /// /// This is a "catch all" type; it must be transmuted to the correct type /// before being called! -pub type Imp = unsafe extern "C" fn(); +pub type Imp = InnerImp; impl Sel { #[inline] From fa3fd4c50d1b5b21d6e5cd940d657921012e8ab6 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sat, 11 Jun 2022 23:46:59 +0200 Subject: [PATCH 2/3] Allow "C-unwind" in ClassBuilder::root --- objc2/CHANGELOG.md | 3 +++ objc2/src/declare.rs | 5 ++++- objc2/src/test_utils.rs | 6 +++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/objc2/CHANGELOG.md b/objc2/CHANGELOG.md index 992fff16d..4beaab3bd 100644 --- a/objc2/CHANGELOG.md +++ b/objc2/CHANGELOG.md @@ -34,6 +34,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * **BREAKING:** `Sel` is now required to be non-null, which means that you have to ensure that any selectors you receive from method calls are non-null before using them. +* **BREAKING**: `ClassBuilder::root` is now generic over the function pointer, + meaning you will have to coerce initializer functions to pointers like in + `ClassBuilder::add_method` before you can use it. ### Removed * **BREAKING:** Removed the `Sel::from_ptr` and `Sel::as_ptr` methods. diff --git a/objc2/src/declare.rs b/objc2/src/declare.rs index 42c32a4ab..4c18aaba1 100644 --- a/objc2/src/declare.rs +++ b/objc2/src/declare.rs @@ -226,7 +226,10 @@ impl ClassBuilder { /// the entire `NSObject` protocol is implemented. /// Functionality it expects, like implementations of `-retain` and /// `-release` used by ARC, will not be present otherwise. - pub fn root(name: &str, intitialize_fn: extern "C" fn(&Class, Sel)) -> Option { + pub fn root(name: &str, intitialize_fn: F) -> Option + where + F: MethodImplementation, + { Self::with_superclass(name, None).map(|mut this| { unsafe { this.add_class_method(sel!(initialize), intitialize_fn) }; this diff --git a/objc2/src/test_utils.rs b/objc2/src/test_utils.rs index 4e724bc4b..b0acbcfd7 100644 --- a/objc2/src/test_utils.rs +++ b/objc2/src/test_utils.rs @@ -92,7 +92,11 @@ pub(crate) fn custom_class() -> &'static Class { // The runtime will call this method, so it has to be implemented extern "C" fn custom_obj_class_initialize(_this: &Class, _cmd: Sel) {} - let mut builder = ClassBuilder::root("CustomObject", custom_obj_class_initialize).unwrap(); + let mut builder = ClassBuilder::root( + "CustomObject", + custom_obj_class_initialize as extern "C" fn(&Class, Sel), + ) + .unwrap(); let proto = custom_protocol(); builder.add_protocol(proto); From bb25515510f1d66c3073d9a30cdfa5ee95f3a27b Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sat, 11 Jun 2022 23:41:33 +0200 Subject: [PATCH 3/3] Add workaround for `extern "C-unwind"` not implementing common traits --- objc-sys/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/objc-sys/src/lib.rs b/objc-sys/src/lib.rs index 2ad87df62..53f4f77e9 100644 --- a/objc-sys/src/lib.rs +++ b/objc-sys/src/lib.rs @@ -71,6 +71,10 @@ macro_rules! generate_linking_tests { // Get function pointer to make the linker require the // symbol to be available. let f: unsafe extern $abi fn($($t),*) $(-> $r)? = crate::$name; + // Workaround for https://github.com/rust-lang/rust/pull/92964 + #[cfg(feature = "unstable-c-unwind")] + #[allow(clippy::useless_transmute)] + let f: unsafe extern "C" fn() = unsafe { core::mem::transmute(f) }; // Execute side-effect to ensure it is not optimized away. std::println!("{:p}", f); }