Skip to content

Commit

Permalink
Merge pull request #567 from madsmtm/ignore-signed
Browse files Browse the repository at this point in the history
Ignore signed-ness in encoding
  • Loading branch information
madsmtm authored May 20, 2024
2 parents 4e3e43d + 9090fa6 commit 6b30466
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 9 deletions.
3 changes: 3 additions & 0 deletions crates/objc2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

### Added
* Added `Id::autorelease_ptr`.
* Added the feature flag `"relax-sign-encoding"`, which when enabled, allows
using e.g. `NSInteger` in places where you would otherwise have to use
`NSUInteger`.

### Deprecated
* Deprecated the `apple` Cargo feature flag, it is assumed by default on Apple
Expand Down
7 changes: 7 additions & 0 deletions crates/objc2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ catch-all = ["exception"]
# to objc2.
relax-void-encoding = []

# Make signed and unsigned types interchangable when used as arguments/return
# types in methods.
#
# This may be useful for dealing with Swift code that incorrectly uses `Int`
# instead of `UInt`.
relax-sign-encoding = []

# Enable deprecation of using `msg_send!` without a comma between arguments.
unstable-msg-send-always-comma = []

Expand Down
6 changes: 3 additions & 3 deletions crates/objc2/src/runtime/declare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -680,13 +680,13 @@ mod tests {
#[test]
#[cfg_attr(
debug_assertions,
should_panic = "declared invalid method -[TestClassBuilderInvalidMethod foo]: expected return to have type code 'I', but found 'i'"
should_panic = "declared invalid method -[TestClassBuilderInvalidMethod foo]: expected return to have type code 'I', but found 's'"
)]
fn invalid_method() {
let cls = test_utils::custom_class();
let mut builder = ClassBuilder::new("TestClassBuilderInvalidMethod", cls).unwrap();

extern "C" fn foo(_this: &NSObject, _cmd: Sel) -> i32 {
extern "C" fn foo(_this: &NSObject, _cmd: Sel) -> i16 {
0
}

Expand All @@ -697,7 +697,7 @@ mod tests {

#[test]
#[cfg_attr(
debug_assertions,
all(debug_assertions, not(feature = "relax-sign-encoding")),
should_panic = "declared invalid method +[TestClassBuilderInvalidClassMethod classFoo]: expected return to have type code 'I', but found 'i'"
)]
fn invalid_class_method() {
Expand Down
7 changes: 7 additions & 0 deletions crates/objc2/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ pub(crate) fn custom_class() -> &'static AnyClass {
7
}

extern "C" fn get_nsinteger(_this: &AnyObject, _cmd: Sel) -> ffi::NSInteger {
5
}

extern "C" fn custom_obj_set_bar(this: &mut AnyObject, _cmd: Sel, bar: u32) {
let ivar = this.class().instance_variable("_bar").unwrap();
unsafe { *ivar.load_mut::<u32>(this) = bar }
Expand Down Expand Up @@ -183,6 +187,9 @@ pub(crate) fn custom_class() -> &'static AnyClass {
let class_method: extern "C" fn(_, _) -> _ = custom_obj_class_method;
builder.add_class_method(sel!(classFoo), class_method);

let get_nsinteger: extern "C" fn(_, _) -> _ = get_nsinteger;
builder.add_method(sel!(getNSInteger), get_nsinteger);

let protocol_instance_method: extern "C" fn(_, _, _) = custom_obj_set_bar;
builder.add_method(sel!(setBar:), protocol_instance_method);
let protocol_class_method: extern "C" fn(_, _, _, _) -> _ =
Expand Down
47 changes: 41 additions & 6 deletions crates/objc2/src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ impl fmt::Display for VerificationError {
impl Error for VerificationError {}

/// Relaxed version of `Encoding::equivalent_to_box` that allows
/// `*mut c_void` and `*const c_void` to be used in place of other pointers.
/// `*mut c_void` and `*const c_void` to be used in place of other pointers,
/// and allows signed types where unsigned types are excepted.
///
/// Note: This is a top-level comparison; `*mut *mut c_void` or structures
/// containing `*mut c_void` are not allowed differently than usual.
Expand All @@ -81,10 +82,32 @@ fn relaxed_equivalent_to_box(encoding: &Encoding, expected: &EncodingBox) -> boo
&& matches!(encoding, Encoding::Pointer(&Encoding::Void))
&& matches!(expected, EncodingBox::Pointer(_))
{
true
} else {
encoding.equivalent_to_box(expected)
return true;
}

if cfg!(feature = "relax-sign-encoding") {
let actual_signed = match encoding {
Encoding::UChar => &Encoding::Char,
Encoding::UShort => &Encoding::Short,
Encoding::UInt => &Encoding::Int,
Encoding::ULong => &Encoding::Long,
Encoding::ULongLong => &Encoding::LongLong,
enc => enc,
};
let expected_signed = match expected {
EncodingBox::UChar => &EncodingBox::Char,
EncodingBox::UShort => &EncodingBox::Short,
EncodingBox::UInt => &EncodingBox::Int,
EncodingBox::ULong => &EncodingBox::Long,
EncodingBox::ULongLong => &EncodingBox::LongLong,
enc => enc,
};
if actual_signed == expected_signed {
return true;
}
}

encoding.equivalent_to_box(expected)
}

pub(crate) fn verify_method_signature(
Expand Down Expand Up @@ -133,6 +156,7 @@ pub(crate) fn verify_method_signature(
#[cfg(test)]
mod tests {
use super::*;
use crate::ffi;
use crate::runtime::Sel;
use crate::test_utils;
use crate::{msg_send, sel};
Expand Down Expand Up @@ -183,6 +207,17 @@ mod tests {
"expected argument at index 0 to have type code 'I', but found ':'"
);

// <https://github.com/madsmtm/objc2/issues/566>
let res = cls.verify_sel::<(), ffi::NSUInteger>(sel!(getNSInteger));
let expected = if cfg!(feature = "relax-sign-encoding") {
Ok(())
} else if cfg!(target_pointer_width = "64") {
Err("expected return to have type code 'q', but found 'Q'".to_string())
} else {
Err("expected return to have type code 'i', but found 'I'".to_string())
};
assert_eq!(res.map_err(|e| e.to_string()), expected);

// Metaclass
let metaclass = cls.metaclass();
let err = metaclass
Expand All @@ -193,10 +228,10 @@ mod tests {

#[test]
#[cfg(debug_assertions)]
#[should_panic = "invalid message send to -[CustomObject foo]: expected return to have type code 'I', but found 'i'"]
#[should_panic = "invalid message send to -[CustomObject foo]: expected return to have type code 'I', but found '^i'"]
fn test_send_message_verified() {
let obj = test_utils::custom_object();
let _: i32 = unsafe { msg_send![&obj, foo] };
let _: *const i32 = unsafe { msg_send![&obj, foo] };
}

#[test]
Expand Down

0 comments on commit 6b30466

Please sign in to comment.