From 37a60b9502129817f93d77cfb16863d6fcaab3ca Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Tue, 4 Oct 2022 16:20:44 +0200 Subject: [PATCH] TMP --- objc2/src/__macro_helpers.rs | 178 +++++++++++++++++---------- objc2/src/foundation/string.rs | 11 +- objc2/src/macros.rs | 162 +++++++++++++++--------- objc2/src/macros/__msg_send_parse.rs | 95 ++++++++++++++ objc2/src/macros/extern_methods.rs | 17 +++ objc2/tests/use_macros.rs | 2 - test-ui/ui/invalid_msg_send.rs | 1 + 7 files changed, 335 insertions(+), 131 deletions(-) create mode 100644 objc2/src/macros/__msg_send_parse.rs diff --git a/objc2/src/__macro_helpers.rs b/objc2/src/__macro_helpers.rs index 3de840cb4..07f32d272 100644 --- a/objc2/src/__macro_helpers.rs +++ b/objc2/src/__macro_helpers.rs @@ -1,12 +1,13 @@ #[cfg(feature = "verify_message")] use alloc::vec::Vec; +use core::ptr; #[cfg(feature = "verify_message")] use std::collections::HashSet; use crate::declare::ClassBuilder; #[cfg(feature = "verify_message")] use crate::declare::MethodImplementation; -use crate::rc::{Allocated, Id, Ownership}; +use crate::rc::{Allocated, Id, Ownership, Shared}; #[cfg(feature = "verify_message")] use crate::runtime::MethodDescription; use crate::runtime::{Class, Object, Protocol, Sel}; @@ -352,6 +353,34 @@ impl<'a> MsgSendIdFailed<'a> for Other { } } +#[track_caller] +pub unsafe fn handle_error_id( + f: impl FnOnce(Option<&mut *mut E>) -> Option>, +) -> Result, Id> { + let mut err = ptr::null_mut(); + if let Some(res) = f(Some(&mut err)) { + Ok(res) + } else { + // SAFETY: TODO + Err(unsafe { Id::new(err) } + .expect("error parameter should be set if the method returns NULL")) + } +} + +#[track_caller] +pub unsafe fn handle_error_bool( + f: impl FnOnce(Option<&mut *mut E>) -> bool, +) -> Result<(), Id> { + let mut err = ptr::null_mut(); + if f(Some(&mut err)) { + Ok(()) + } else { + // SAFETY: TODO + Err(unsafe { Id::new(err) } + .expect("error parameter should be set if the method returns NO")) + } +} + /// Checks whether a given selector is said to be in a given selector family. /// /// @@ -555,6 +584,7 @@ impl Drop for ClassProtocolMethodsBuilder<'_, '_> { mod tests { use super::*; + use alloc::string::ToString; use alloc::vec; use core::ptr; @@ -777,80 +807,92 @@ mod tests { #[test] fn test_in_selector_family() { + fn assert_in_family(selector: &str, family: &str) { + assert!(in_selector_family(selector.as_bytes(), family.as_bytes())); + let selector = selector.to_string() + "\0"; + assert!(in_selector_family(selector.as_bytes(), family.as_bytes())); + } + + fn assert_not_in_family(selector: &str, family: &str) { + assert!(!in_selector_family(selector.as_bytes(), family.as_bytes())); + let selector = selector.to_string() + "\0"; + assert!(!in_selector_family(selector.as_bytes(), family.as_bytes())); + } + // Common cases - assert!(in_selector_family(b"alloc", b"alloc")); - assert!(in_selector_family(b"allocWithZone:", b"alloc")); - assert!(!in_selector_family(b"dealloc", b"alloc")); - assert!(!in_selector_family(b"initialize", b"init")); - assert!(!in_selector_family(b"decimalNumberWithDecimal:", b"init")); - assert!(in_selector_family(b"initWithCapacity:", b"init")); - assert!(in_selector_family(b"_initButPrivate:withParam:", b"init")); - assert!(!in_selector_family(b"description", b"init")); - assert!(!in_selector_family(b"inIT", b"init")); - - assert!(!in_selector_family(b"init", b"copy")); - assert!(!in_selector_family(b"copyingStuff:", b"copy")); - assert!(in_selector_family(b"copyWithZone:", b"copy")); - assert!(!in_selector_family(b"initWithArray:copyItems:", b"copy")); - assert!(in_selector_family(b"copyItemAtURL:toURL:error:", b"copy")); - - assert!(!in_selector_family(b"mutableCopying", b"mutableCopy")); - assert!(in_selector_family(b"mutableCopyWithZone:", b"mutableCopy")); - assert!(in_selector_family(b"mutableCopyWithZone:", b"mutableCopy")); - - assert!(in_selector_family( - b"newScriptingObjectOfClass:forValueForKey:withContentsValue:properties:", - b"new" - )); - assert!(in_selector_family( - b"newScriptingObjectOfClass:forValueForKey:withContentsValue:properties:", - b"new" - )); - assert!(!in_selector_family(b"newsstandAssetDownload", b"new")); + assert_in_family("alloc", "alloc"); + assert_in_family("allocWithZone:", "alloc"); + assert_not_in_family("dealloc", "alloc"); + assert_not_in_family("initialize", "init"); + assert_not_in_family("decimalNumberWithDecimal:", "init"); + assert_in_family("initWithCapacity:", "init"); + assert_in_family("_initButPrivate:withParam:", "init"); + assert_not_in_family("description", "init"); + assert_not_in_family("inIT", "init"); + + assert_not_in_family("init", "copy"); + assert_not_in_family("copyingStuff:", "copy"); + assert_in_family("copyWithZone:", "copy"); + assert_not_in_family("initWithArray:copyItems:", "copy"); + assert_in_family("copyItemAtURL:toURL:error:", "copy"); + + assert_not_in_family("mutableCopying", "mutableCopy"); + assert_in_family("mutableCopyWithZone:", "mutableCopy"); + assert_in_family("mutableCopyWithZone:", "mutableCopy"); + + assert_in_family( + "newScriptingObjectOfClass:forValueForKey:withContentsValue:properties:", + "new", + ); + assert_in_family( + "newScriptingObjectOfClass:forValueForKey:withContentsValue:properties:", + "new", + ); + assert_not_in_family("newsstandAssetDownload", "new"); // Trying to weed out edge-cases: - assert!(in_selector_family(b"__abcDef", b"abc")); - assert!(in_selector_family(b"_abcDef", b"abc")); - assert!(in_selector_family(b"abcDef", b"abc")); - assert!(in_selector_family(b"___a", b"a")); - assert!(in_selector_family(b"__a", b"a")); - assert!(in_selector_family(b"_a", b"a")); - assert!(in_selector_family(b"a", b"a")); - - assert!(!in_selector_family(b"_abcdef", b"abc")); - assert!(!in_selector_family(b"_abcdef", b"def")); - assert!(!in_selector_family(b"_bcdef", b"abc")); - assert!(!in_selector_family(b"a_bc", b"abc")); - assert!(!in_selector_family(b"abcdef", b"abc")); - assert!(!in_selector_family(b"abcdef", b"def")); - assert!(!in_selector_family(b"abcdef", b"abb")); - assert!(!in_selector_family(b"___", b"a")); - assert!(!in_selector_family(b"_", b"a")); - assert!(!in_selector_family(b"", b"a")); - - assert!(in_selector_family(b"copy", b"copy")); - assert!(in_selector_family(b"copy:", b"copy")); - assert!(in_selector_family(b"copyMe", b"copy")); - assert!(in_selector_family(b"_copy", b"copy")); - assert!(in_selector_family(b"_copy:", b"copy")); - assert!(in_selector_family(b"_copyMe", b"copy")); - assert!(!in_selector_family(b"copying", b"copy")); - assert!(!in_selector_family(b"copying:", b"copy")); - assert!(!in_selector_family(b"_copying", b"copy")); - assert!(!in_selector_family(b"Copy", b"copy")); - assert!(!in_selector_family(b"COPY", b"copy")); + assert_in_family("__abcDef", "abc"); + assert_in_family("_abcDef", "abc"); + assert_in_family("abcDef", "abc"); + assert_in_family("___a", "a"); + assert_in_family("__a", "a"); + assert_in_family("_a", "a"); + assert_in_family("a", "a"); + + assert_not_in_family("_abcdef", "abc"); + assert_not_in_family("_abcdef", "def"); + assert_not_in_family("_bcdef", "abc"); + assert_not_in_family("a_bc", "abc"); + assert_not_in_family("abcdef", "abc"); + assert_not_in_family("abcdef", "def"); + assert_not_in_family("abcdef", "abb"); + assert_not_in_family("___", "a"); + assert_not_in_family("_", "a"); + assert_not_in_family("", "a"); + + assert_in_family("copy", "copy"); + assert_in_family("copy:", "copy"); + assert_in_family("copyMe", "copy"); + assert_in_family("_copy", "copy"); + assert_in_family("_copy:", "copy"); + assert_in_family("_copyMe", "copy"); + assert_not_in_family("copying", "copy"); + assert_not_in_family("copying:", "copy"); + assert_not_in_family("_copying", "copy"); + assert_not_in_family("Copy", "copy"); + assert_not_in_family("COPY", "copy"); // Empty family (not supported) - assert!(in_selector_family(b"___", b"")); - assert!(in_selector_family(b"__", b"")); - assert!(in_selector_family(b"_", b"")); - assert!(in_selector_family(b"", b"")); - assert!(!in_selector_family(b"_a", b"")); - assert!(!in_selector_family(b"a", b"")); - assert!(in_selector_family(b"_A", b"")); - assert!(in_selector_family(b"A", b"")); + assert_in_family("___", ""); + assert_in_family("__", ""); + assert_in_family("_", ""); + assert_in_family("", ""); + assert_not_in_family("_a", ""); + assert_not_in_family("a", ""); + assert_in_family("_A", ""); + assert_in_family("A", ""); } mod test_trait_disambugated { diff --git a/objc2/src/foundation/string.rs b/objc2/src/foundation/string.rs index 3962c4f60..c56109019 100644 --- a/objc2/src/foundation/string.rs +++ b/objc2/src/foundation/string.rs @@ -9,7 +9,7 @@ use core::slice; use core::str; use std::os::raw::c_char; -use super::{NSComparisonResult, NSCopying, NSMutableCopying, NSMutableString, NSObject}; +use super::{NSComparisonResult, NSCopying, NSError, NSMutableCopying, NSMutableString, NSObject}; use crate::rc::{autoreleasepool, AutoreleasePool, DefaultId, Id, Shared}; use crate::runtime::{Class, Object}; use crate::{extern_class, extern_methods, msg_send, msg_send_id, ClassType}; @@ -272,6 +272,15 @@ extern_methods!( // pub fn from_nsrange(range: NSRange) -> Id // https://developer.apple.com/documentation/foundation/1415155-nsstringfromrange?language=objc + + // TODO: Safety + #[sel(writeToFile:atomically:encoding:error:)] + pub unsafe fn write_to_file( + &self, + path: &NSString, + atomically: bool, + encoding: usize, + ) -> Result<(), Id>; } ); diff --git a/objc2/src/macros.rs b/objc2/src/macros.rs index 368efb0a8..65e50fbae 100644 --- a/objc2/src/macros.rs +++ b/objc2/src/macros.rs @@ -1,3 +1,4 @@ +mod __msg_send_parse; mod __rewrite_self_arg; mod declare_class; mod extern_class; @@ -772,43 +773,62 @@ macro_rules! __class_inner { /// ``` #[macro_export] macro_rules! msg_send { - [super($obj:expr), $selector:ident $(,)?] => ({ - let sel = $crate::sel!($selector); - let result; - // Note: `sel` and `result` can be accessed from the `obj` and - // `superclass` expressions - we won't (yet) bother with preventing - // that though. - result = $crate::MessageReceiver::__send_super_message_static($obj, sel, ()); - result - }); - [super($obj:expr), $($selector:ident : $argument:expr),+ $(,)?] => ({ - let sel = $crate::sel!($($selector :)+); - let result; - result = $crate::MessageReceiver::__send_super_message_static($obj, sel, ($($argument,)+)); - result - }); - [super($obj:expr, $superclass:expr), $selector:ident $(,)?] => ({ - let sel = $crate::sel!($selector); - let result; - result = $crate::MessageReceiver::send_super_message($obj, $superclass, sel, ()); - result - }); - [super($obj:expr, $superclass:expr), $($selector:ident : $argument:expr $(,)?)+] => ({ - let sel = $crate::sel!($($selector :)+); - let result; - result = $crate::MessageReceiver::send_super_message($obj, $superclass, sel, ($($argument,)+)); - result - }); - [$obj:expr, $selector:ident $(,)?] => ({ - let sel = $crate::sel!($selector); - let result; - result = $crate::MessageReceiver::send_message($obj, sel, ()); - result - }); - [$obj:expr, $($selector:ident : $argument:expr $(,)?)+] => ({ - let sel = $crate::sel!($($selector :)+); + [super($obj:expr), $($selector_and_arguments:tt)+] => { + $crate::__msg_send_parse! { + ($crate::__msg_send_helper) + @($crate::__macro_helpers::handle_error_bool) + @() + @() + @($($selector_and_arguments)+) + + @($crate::MessageReceiver::__send_super_message_static) + @($obj) + } + }; + [super($obj:expr, $superclass:expr), $($selector_and_arguments:tt)+] => { + $crate::__msg_send_parse! { + ($crate::__msg_send_helper) + @($crate::__macro_helpers::handle_error_bool) + @() + @() + @($($selector_and_arguments)+) + + @($crate::MessageReceiver::send_super_message) + @($obj, $superclass) + } + }; + [$obj:expr, $($selector_and_arguments:tt)+] => { + $crate::__msg_send_parse! { + ($crate::__msg_send_helper) + @($crate::__macro_helpers::handle_error_bool) + @() + @() + @($($selector_and_arguments)+) + + @($crate::MessageReceiver::send_message) + @($obj) + } + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __msg_send_helper { + { + @($fn:expr) + @($($fn_args:tt)+) + @($($selector:tt)*) + @($($argument:expr,)*) + } => ({ + // Assign to intermediary variable for better UI, and to prevent + // miscompilation on older Rust versions. + // + // Note: This can be accessed from any expression in `fn_args` and + // `arguments` - we won't (yet) bother with preventing that though. let result; - result = $crate::MessageReceiver::send_message($obj, sel, ($($argument,)+)); + // Always add trailing comma after each argument, so that we get a + // 1-tuple if there is only one. + result = $fn($($fn_args)+, $crate::sel!($($selector)*), ($($argument,)*)); result }); } @@ -992,46 +1012,68 @@ macro_rules! msg_send_id { result = <$crate::__macro_helpers::Init as $crate::__macro_helpers::MsgSendId<_, _>>::send_message_id($obj, sel, ()); result }); - [$obj:expr, $selector:ident $(,)?] => ({ - $crate::__msg_send_id_helper!(@verify $selector); - let sel = $crate::sel!($selector); - const NAME: &$crate::__macro_helpers::str = $crate::__macro_helpers::stringify!($selector); - let result; - result = <$crate::__macro_helpers::RetainSemantics<{ - $crate::__macro_helpers::retain_semantics(NAME) - }> as $crate::__macro_helpers::MsgSendId<_, _>>::send_message_id($obj, sel, ()); - result - }); - [$obj:expr, $($selector:ident : $argument:expr),+ $(,)?] => ({ - let sel = $crate::sel!($($selector:)+); - const NAME: &$crate::__macro_helpers::str = - $crate::__macro_helpers::concat!($($crate::__macro_helpers::stringify!($selector), ':'),+); - let result; - result = <$crate::__macro_helpers::RetainSemantics<{ - $crate::__macro_helpers::retain_semantics(NAME) - }> as $crate::__macro_helpers::MsgSendId<_, _>>::send_message_id($obj, sel, ($($argument,)+)); - result - }); + [$obj:expr, $($selector_and_arguments:tt)+] => { + $crate::__msg_send_parse! { + ($crate::__msg_send_id_helper) + @($crate::__macro_helpers::handle_error_id) + @() + @() + @($($selector_and_arguments)+) + + @($obj) + } + }; } /// Helper macro to avoid exposing these in the docs for [`msg_send_id!`]. #[doc(hidden)] #[macro_export] macro_rules! __msg_send_id_helper { - (@verify retain) => {{ + { + @($obj:expr) + @(retain) + @() + } => {{ $crate::__macro_helpers::compile_error!( "msg_send_id![obj, retain] is not supported. Use `Id::retain` instead" ) }}; - (@verify release) => {{ + { + @($obj:expr) + @(release) + @() + } => {{ $crate::__macro_helpers::compile_error!( "msg_send_id![obj, release] is not supported. Drop an `Id` instead" ) }}; - (@verify autorelease) => {{ + { + @($obj:expr) + @(autorelease) + @() + } => {{ $crate::__macro_helpers::compile_error!( "msg_send_id![obj, autorelease] is not supported. Use `Id::autorelease`" ) }}; - (@verify $selector:ident) => {{}}; + { + @($obj:expr) + @($($selector:tt)*) + @($($argument:expr,)*) + } => ({ + // Don't use `sel!`, otherwise we'd end up with defining this data twice. + const __SELECTOR_DATA: &$crate::__macro_helpers::str = $crate::__sel_data!($($selector)*); + let result; + result = <$crate::__macro_helpers::RetainSemantics<{ + $crate::__macro_helpers::retain_semantics(__SELECTOR_DATA) + }> as $crate::__macro_helpers::MsgSendId<_, _>>::send_message_id( + $obj, + $crate::__sel_inner!( + __SELECTOR_DATA, + $crate::__hash_idents!($($selector)*) + ), + ($($argument,)*), + ); + result + }); } diff --git a/objc2/src/macros/__msg_send_parse.rs b/objc2/src/macros/__msg_send_parse.rs new file mode 100644 index 000000000..e36924f6f --- /dev/null +++ b/objc2/src/macros/__msg_send_parse.rs @@ -0,0 +1,95 @@ +#[doc(hidden)] +#[macro_export] +macro_rules! __msg_send_parse { + // No arguments + { + ($out_macro:path) + @($error_handler:path) + // Intentionally empty + @() + @() + @($selector:ident $(,)?) + $($macro_args:tt)* + } => { + $crate::__msg_send_parse! { + ($out_macro) + @($error_handler) + @($selector) + @() + @() + $($macro_args)* + } + }; + + // tt-munch remaining `selector: argument` pairs, looking for a pattern + // that ends with `error: _`. + { + ($out_macro:path) + @($_error_handler:path) + @($($selector_output:tt)*) + @($($argument_output:tt)*) + @() + $($macro_args:tt)* + } => ({ + $out_macro! { + $($macro_args)* + @($($selector_output)*) + @($($argument_output)*) + } + }); + { + ($out_macro:path) + @($error_handler:path) + @($($selector_output:tt)*) + @($($argument_output:tt)*) + @(error: _ $(,)?) + $($macro_args:tt)* + } => { + $error_handler(|__error| $crate::__msg_send_parse! { + ($out_macro) + @($error_handler) + @($($selector_output)* error:) + @($($argument_output)* __error,) + @() + $($macro_args)* + }) + }; + { + ($out_macro:path) + @($error_handler:path) + @($($selector_output:tt)*) + @($($argument_output:tt)*) + @($selector:ident : $argument:expr $(, $($rest:tt)*)?) + $($macro_args:tt)* + } => { + $crate::__msg_send_parse! { + ($out_macro) + @($error_handler) + @($($selector_output)* $selector:) + @($($argument_output)* $argument,) + @($($($rest)*)?) + $($macro_args)* + } + }; + + // Handle calls without comma between `selector: argument` pair. + // TODO: Deprecate this + { + ($out_macro:path) + @($error_handler:path) + // Intentionally empty + @() + @() + @($($selector:ident : $argument:expr)*) + $($macro_args:tt)* + } => { + $crate::__msg_send_parse! { + ($out_macro) + @($error_handler) + @() + @() + @($($selector : $argument),*) + $($macro_args)* + } + }; +} diff --git a/objc2/src/macros/extern_methods.rs b/objc2/src/macros/extern_methods.rs index 45c32050f..ecc6c1756 100644 --- a/objc2/src/macros/extern_methods.rs +++ b/objc2/src/macros/extern_methods.rs @@ -370,6 +370,23 @@ macro_rules! __collect_msg_send { $macro![$obj, $($output)+] }}; + // Allow trailing `error:` sel without a corresponding argument + ( + $macro:path; + $obj:expr; + (error:); + ($(,)?); + $($output:tt)* + ) => { + $crate::__collect_msg_send! { + $macro; + $obj; + (); + (); + $($output)* error: _, + } + }; + // tt-munch each argument ( $macro:path; diff --git a/objc2/tests/use_macros.rs b/objc2/tests/use_macros.rs index 3f7c6418d..cb1fdf6f3 100644 --- a/objc2/tests/use_macros.rs +++ b/objc2/tests/use_macros.rs @@ -32,7 +32,6 @@ fn test_msg_send_comma_handling(obj: &NSString, superclass: &Class) { let _: () = msg_send![obj, a: 32i32]; let _: () = msg_send![obj, a: 32i32,]; let _: () = msg_send![obj, a: 32i32 b: 32i32]; - let _: () = msg_send![obj, a: 32i32 b: 32i32,]; let _: () = msg_send![obj, a: 32i32, b: 32i32]; let _: () = msg_send![obj, a: 32i32, b: 32i32,]; } @@ -43,7 +42,6 @@ fn test_msg_send_comma_handling(obj: &NSString, superclass: &Class) { let _: () = msg_send![super(obj, superclass), a: 32i32]; let _: () = msg_send![super(obj, superclass), a: 32i32,]; let _: () = msg_send![super(obj, superclass), a: 32i32 b: 32i32]; - let _: () = msg_send![super(obj, superclass), a: 32i32 b: 32i32,]; let _: () = msg_send![super(obj, superclass), a: 32i32, b: 32i32]; let _: () = msg_send![super(obj, superclass), a: 32i32, b: 32i32,]; } diff --git a/test-ui/ui/invalid_msg_send.rs b/test-ui/ui/invalid_msg_send.rs index 21e493c3c..8995bd242 100644 --- a/test-ui/ui/invalid_msg_send.rs +++ b/test-ui/ui/invalid_msg_send.rs @@ -13,4 +13,5 @@ fn main() { let _: () = unsafe { msg_send![obj, a: b: c] }; let _: () = unsafe { msg_send![obj, a: b, c d] }; let _: () = unsafe { msg_send![obj, a: b: c] }; + let _: () = unsafe { msg_send![obj, a: b c: d,] }; }