diff --git a/crates/objc2/src/declare.rs b/crates/objc2/src/declare.rs index 2e80ecb9b..5f63809ef 100644 --- a/crates/objc2/src/declare.rs +++ b/crates/objc2/src/declare.rs @@ -357,15 +357,17 @@ impl ClassBuilder { T: Message + ?Sized, F: MethodImplementation, { - let encs = F::Args::ENCODINGS; + let enc_args = F::Args::ENCODINGS; + let enc_ret = F::Ret::ENCODING; + let sel_args = count_args(sel); assert_eq!( sel_args, - encs.len(), + enc_args.len(), "Selector {:?} accepts {} arguments, but function accepts {}", sel, sel_args, - encs.len(), + enc_args.len(), ); // Verify that, if the method is present on the superclass, that the @@ -373,14 +375,14 @@ impl ClassBuilder { #[cfg(debug_assertions)] if let Some(superclass) = self.superclass() { if let Some(method) = superclass.instance_method(sel) { - if let Err(err) = crate::verify::verify_method_signature::(method) + if let Err(err) = crate::verify::verify_method_signature(method, enc_args, &enc_ret) { panic!("declared invalid method -[{} {sel:?}]: {err}", self.name()) } } } - let types = method_type_encoding(&F::Ret::ENCODING, encs); + let types = method_type_encoding(&enc_ret, enc_args); let success = Bool::from_raw(unsafe { ffi::class_addMethod( self.as_mut_ptr(), @@ -412,15 +414,17 @@ impl ClassBuilder { where F: MethodImplementation, { - let encs = F::Args::ENCODINGS; + let enc_args = F::Args::ENCODINGS; + let enc_ret = F::Ret::ENCODING; + let sel_args = count_args(sel); assert_eq!( sel_args, - encs.len(), + enc_args.len(), "Selector {:?} accepts {} arguments, but function accepts {}", sel, sel_args, - encs.len(), + enc_args.len(), ); // Verify that, if the method is present on the superclass, that the @@ -428,14 +432,14 @@ impl ClassBuilder { #[cfg(debug_assertions)] if let Some(superclass) = self.superclass() { if let Some(method) = superclass.class_method(sel) { - if let Err(err) = crate::verify::verify_method_signature::(method) + if let Err(err) = crate::verify::verify_method_signature(method, enc_args, &enc_ret) { panic!("declared invalid method +[{} {sel:?}]: {err}", self.name()) } } } - let types = method_type_encoding(&F::Ret::ENCODING, encs); + let types = method_type_encoding(&enc_ret, enc_args); let success = Bool::from_raw(unsafe { ffi::class_addMethod( self.metaclass_mut(), diff --git a/crates/objc2/src/message/apple/mod.rs b/crates/objc2/src/message/apple/mod.rs index 48b5d607e..66a0c73f5 100644 --- a/crates/objc2/src/message/apple/mod.rs +++ b/crates/objc2/src/message/apple/mod.rs @@ -1,4 +1,3 @@ -use super::conditional_try; use crate::encode::Encode; use crate::ffi; use crate::runtime::{Class, Imp, Object, Sel}; @@ -33,7 +32,7 @@ where R: Encode, { let msg_send_fn = R::MSG_SEND; - unsafe { conditional_try(|| A::__invoke(msg_send_fn, receiver, sel, args)) } + unsafe { conditional_try!(|| A::__invoke(msg_send_fn, receiver, sel, args)) } } #[inline] @@ -57,5 +56,5 @@ where let receiver = receiver.cast(); let msg_send_fn = R::MSG_SEND_SUPER; - unsafe { conditional_try(|| A::__invoke(msg_send_fn, receiver, sel, args)) } + unsafe { conditional_try!(|| A::__invoke(msg_send_fn, receiver, sel, args)) } } diff --git a/crates/objc2/src/message/gnustep.rs b/crates/objc2/src/message/gnustep.rs index 02a5a6d6c..a73b85720 100644 --- a/crates/objc2/src/message/gnustep.rs +++ b/crates/objc2/src/message/gnustep.rs @@ -1,7 +1,6 @@ use core::hint; use core::mem; -use super::conditional_try; use crate::encode::Encode; use crate::ffi; use crate::runtime::{Class, Imp, Object, Sel}; @@ -39,7 +38,7 @@ where let msg_send_fn = unsafe { ffi::objc_msg_lookup(receiver.cast(), sel.as_ptr()) }; let msg_send_fn = unwrap_msg_send_fn(msg_send_fn); - unsafe { conditional_try(|| A::__invoke(msg_send_fn, receiver, sel, args)) } + unsafe { conditional_try!(|| A::__invoke(msg_send_fn, receiver, sel, args)) } } #[track_caller] @@ -65,5 +64,5 @@ where }; let msg_send_fn = unsafe { ffi::objc_msg_lookup_super(&sup, sel.as_ptr()) }; let msg_send_fn = unwrap_msg_send_fn(msg_send_fn); - unsafe { conditional_try(|| A::__invoke(msg_send_fn, receiver, sel, args)) } + unsafe { conditional_try!(|| A::__invoke(msg_send_fn, receiver, sel, args)) } } diff --git a/crates/objc2/src/message/mod.rs b/crates/objc2/src/message/mod.rs index 0eea94172..8fcc05ad3 100644 --- a/crates/objc2/src/message/mod.rs +++ b/crates/objc2/src/message/mod.rs @@ -7,38 +7,78 @@ use crate::rc::{Id, Owned, Ownership, Shared}; use crate::runtime::{Class, Imp, Object, Sel}; use crate::ClassType; +/// Wrap the given closure in `exception::catch` if the `catch-all` feature is +/// enabled. +/// +/// This is a macro to help with monomorphization when the feature is +/// disabled, as well as improving the final stack trace (`#[track_caller]` +/// doesn't really work on closures). +#[cfg(not(feature = "catch-all"))] +macro_rules! conditional_try { + (|| $expr:expr) => { + $expr + }; +} + #[cfg(feature = "catch-all")] -#[track_caller] -unsafe fn conditional_try(f: impl FnOnce() -> R) -> R { - let f = core::panic::AssertUnwindSafe(f); - match unsafe { crate::exception::catch(f) } { - Ok(r) => r, - Err(exception) => { - if let Some(exception) = exception { - panic!("uncaught {:?}", exception) - } else { - panic!("uncaught exception nil") +macro_rules! conditional_try { + (|| $expr:expr) => {{ + let f = core::panic::AssertUnwindSafe(|| $expr); + match crate::exception::catch(f) { + Ok(r) => r, + Err(exception) => { + if let Some(exception) = exception { + panic!("uncaught {exception:?}") + } else { + panic!("uncaught exception nil") + } } } - } + }}; } -#[cfg(not(feature = "catch-all"))] -#[inline] +/// Help with monomorphizing in `icrate` +#[cfg(debug_assertions)] #[track_caller] -unsafe fn conditional_try(f: impl FnOnce() -> R) -> R { - f() +fn msg_send_check( + obj: Option<&Object>, + sel: Sel, + args: &[crate::encode::Encoding], + ret: &crate::encode::Encoding, +) { + use crate::verify::{verify_method_signature, Inner, VerificationError}; + + let cls = if let Some(obj) = obj { + obj.class() + } else { + panic_null(sel) + }; + + let err = if let Some(method) = cls.instance_method(sel) { + if let Err(err) = verify_method_signature(method, args, ret) { + err + } else { + return; + } + } else { + VerificationError::from(Inner::MethodNotFound) + }; + + panic_verify(cls, sel, err); +} + +#[cfg(debug_assertions)] +#[track_caller] +fn panic_null(sel: Sel) -> ! { + panic!("messsaging {sel:?} to nil") } #[cfg(debug_assertions)] #[track_caller] fn panic_verify(cls: &Class, sel: Sel, err: crate::VerificationError) -> ! { panic!( - "invalid message send to {}[{:?} {:?}]: {}", + "invalid message send to {}[{cls:?} {sel:?}]: {err}", if cls.is_metaclass() { "+" } else { "-" }, - cls, - sel, - err ) } @@ -183,16 +223,8 @@ pub unsafe trait MessageReceiver: private::Sealed + Sized { #[cfg(debug_assertions)] { // SAFETY: Caller ensures only valid or NULL pointers. - let this = unsafe { this.as_ref() }; - let cls = if let Some(this) = this { - this.class() - } else { - panic!("messsaging {:?} to nil", sel); - }; - - if let Err(err) = cls.verify_sel::(sel) { - panic_verify(cls, sel, err); - } + let obj = unsafe { this.as_ref() }; + msg_send_check(obj, sel, A::ENCODINGS, &R::__Inner::ENCODING); } unsafe { EncodeConvert::__from_inner(send_unverified(this, sel, args)) } } @@ -230,7 +262,7 @@ pub unsafe trait MessageReceiver: private::Sealed + Sized { #[cfg(debug_assertions)] { if this.is_null() { - panic!("messsaging {:?} to nil", sel); + panic_null(sel); } if let Err(err) = superclass.verify_sel::(sel) { panic_verify(superclass, sel, err); diff --git a/crates/objc2/src/runtime.rs b/crates/objc2/src/runtime.rs index 82bf5de66..a7ce40bc9 100644 --- a/crates/objc2/src/runtime.rs +++ b/crates/objc2/src/runtime.rs @@ -619,7 +619,7 @@ impl Class { R: EncodeConvert, { let method = self.instance_method(sel).ok_or(Inner::MethodNotFound)?; - verify_method_signature::(method) + verify_method_signature(method, A::ENCODINGS, &R::__Inner::ENCODING) } } diff --git a/crates/objc2/src/verify.rs b/crates/objc2/src/verify.rs index 0dd17c5aa..ca85bd638 100644 --- a/crates/objc2/src/verify.rs +++ b/crates/objc2/src/verify.rs @@ -2,7 +2,7 @@ use core::fmt; use core::hash::Hash; use std::error::Error; -use crate::encode::{Encode, EncodeArguments, EncodeConvert, Encoding, EncodingBox}; +use crate::encode::{Encoding, EncodingBox}; use crate::runtime::{EncodingParseError, Method}; #[derive(Debug, PartialEq, Eq, Hash)] @@ -71,26 +71,25 @@ impl fmt::Display for VerificationError { impl Error for VerificationError {} -pub(crate) fn verify_method_signature(method: &Method) -> Result<(), VerificationError> -where - A: EncodeArguments, - R: EncodeConvert, -{ +pub(crate) fn verify_method_signature( + method: &Method, + args: &[Encoding], + ret: &Encoding, +) -> Result<(), VerificationError> { let mut iter = method.types(); // TODO: Verify stack layout let (expected, _stack_layout) = iter.extract_return()?; - let actual = R::__Inner::ENCODING; - if !actual.equivalent_to_box(&expected) { - return Err(Inner::MismatchedReturn(expected, actual).into()); + if !ret.equivalent_to_box(&expected) { + return Err(Inner::MismatchedReturn(expected, ret.clone()).into()); } iter.verify_receiver()?; iter.verify_sel()?; - let actual_count = A::ENCODINGS.len(); + let actual_count = args.len(); - for (i, actual) in A::ENCODINGS.iter().enumerate() { + for (i, actual) in args.iter().enumerate() { if let Some(res) = iter.next() { // TODO: Verify stack layout let (expected, _stack_layout) = res?;