Skip to content

Commit 9090fa6

Browse files
committed
Ignore signed-ness in encoding
1 parent 4e3e43d commit 9090fa6

File tree

5 files changed

+61
-9
lines changed

5 files changed

+61
-9
lines changed

crates/objc2/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
88

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

1215
### Deprecated
1316
* Deprecated the `apple` Cargo feature flag, it is assumed by default on Apple

crates/objc2/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ catch-all = ["exception"]
4242
# to objc2.
4343
relax-void-encoding = []
4444

45+
# Make signed and unsigned types interchangable when used as arguments/return
46+
# types in methods.
47+
#
48+
# This may be useful for dealing with Swift code that incorrectly uses `Int`
49+
# instead of `UInt`.
50+
relax-sign-encoding = []
51+
4552
# Enable deprecation of using `msg_send!` without a comma between arguments.
4653
unstable-msg-send-always-comma = []
4754

crates/objc2/src/runtime/declare.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -680,13 +680,13 @@ mod tests {
680680
#[test]
681681
#[cfg_attr(
682682
debug_assertions,
683-
should_panic = "declared invalid method -[TestClassBuilderInvalidMethod foo]: expected return to have type code 'I', but found 'i'"
683+
should_panic = "declared invalid method -[TestClassBuilderInvalidMethod foo]: expected return to have type code 'I', but found 's'"
684684
)]
685685
fn invalid_method() {
686686
let cls = test_utils::custom_class();
687687
let mut builder = ClassBuilder::new("TestClassBuilderInvalidMethod", cls).unwrap();
688688

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

@@ -697,7 +697,7 @@ mod tests {
697697

698698
#[test]
699699
#[cfg_attr(
700-
debug_assertions,
700+
all(debug_assertions, not(feature = "relax-sign-encoding")),
701701
should_panic = "declared invalid method +[TestClassBuilderInvalidClassMethod classFoo]: expected return to have type code 'I', but found 'i'"
702702
)]
703703
fn invalid_class_method() {

crates/objc2/src/test_utils.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ pub(crate) fn custom_class() -> &'static AnyClass {
119119
7
120120
}
121121

122+
extern "C" fn get_nsinteger(_this: &AnyObject, _cmd: Sel) -> ffi::NSInteger {
123+
5
124+
}
125+
122126
extern "C" fn custom_obj_set_bar(this: &mut AnyObject, _cmd: Sel, bar: u32) {
123127
let ivar = this.class().instance_variable("_bar").unwrap();
124128
unsafe { *ivar.load_mut::<u32>(this) = bar }
@@ -183,6 +187,9 @@ pub(crate) fn custom_class() -> &'static AnyClass {
183187
let class_method: extern "C" fn(_, _) -> _ = custom_obj_class_method;
184188
builder.add_class_method(sel!(classFoo), class_method);
185189

190+
let get_nsinteger: extern "C" fn(_, _) -> _ = get_nsinteger;
191+
builder.add_method(sel!(getNSInteger), get_nsinteger);
192+
186193
let protocol_instance_method: extern "C" fn(_, _, _) = custom_obj_set_bar;
187194
builder.add_method(sel!(setBar:), protocol_instance_method);
188195
let protocol_class_method: extern "C" fn(_, _, _, _) -> _ =

crates/objc2/src/verify.rs

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ impl fmt::Display for VerificationError {
7272
impl Error for VerificationError {}
7373

7474
/// Relaxed version of `Encoding::equivalent_to_box` that allows
75-
/// `*mut c_void` and `*const c_void` to be used in place of other pointers.
75+
/// `*mut c_void` and `*const c_void` to be used in place of other pointers,
76+
/// and allows signed types where unsigned types are excepted.
7677
///
7778
/// Note: This is a top-level comparison; `*mut *mut c_void` or structures
7879
/// containing `*mut c_void` are not allowed differently than usual.
@@ -81,10 +82,32 @@ fn relaxed_equivalent_to_box(encoding: &Encoding, expected: &EncodingBox) -> boo
8182
&& matches!(encoding, Encoding::Pointer(&Encoding::Void))
8283
&& matches!(expected, EncodingBox::Pointer(_))
8384
{
84-
true
85-
} else {
86-
encoding.equivalent_to_box(expected)
85+
return true;
8786
}
87+
88+
if cfg!(feature = "relax-sign-encoding") {
89+
let actual_signed = match encoding {
90+
Encoding::UChar => &Encoding::Char,
91+
Encoding::UShort => &Encoding::Short,
92+
Encoding::UInt => &Encoding::Int,
93+
Encoding::ULong => &Encoding::Long,
94+
Encoding::ULongLong => &Encoding::LongLong,
95+
enc => enc,
96+
};
97+
let expected_signed = match expected {
98+
EncodingBox::UChar => &EncodingBox::Char,
99+
EncodingBox::UShort => &EncodingBox::Short,
100+
EncodingBox::UInt => &EncodingBox::Int,
101+
EncodingBox::ULong => &EncodingBox::Long,
102+
EncodingBox::ULongLong => &EncodingBox::LongLong,
103+
enc => enc,
104+
};
105+
if actual_signed == expected_signed {
106+
return true;
107+
}
108+
}
109+
110+
encoding.equivalent_to_box(expected)
88111
}
89112

90113
pub(crate) fn verify_method_signature(
@@ -133,6 +156,7 @@ pub(crate) fn verify_method_signature(
133156
#[cfg(test)]
134157
mod tests {
135158
use super::*;
159+
use crate::ffi;
136160
use crate::runtime::Sel;
137161
use crate::test_utils;
138162
use crate::{msg_send, sel};
@@ -183,6 +207,17 @@ mod tests {
183207
"expected argument at index 0 to have type code 'I', but found ':'"
184208
);
185209

210+
// <https://github.com/madsmtm/objc2/issues/566>
211+
let res = cls.verify_sel::<(), ffi::NSUInteger>(sel!(getNSInteger));
212+
let expected = if cfg!(feature = "relax-sign-encoding") {
213+
Ok(())
214+
} else if cfg!(target_pointer_width = "64") {
215+
Err("expected return to have type code 'q', but found 'Q'".to_string())
216+
} else {
217+
Err("expected return to have type code 'i', but found 'I'".to_string())
218+
};
219+
assert_eq!(res.map_err(|e| e.to_string()), expected);
220+
186221
// Metaclass
187222
let metaclass = cls.metaclass();
188223
let err = metaclass
@@ -193,10 +228,10 @@ mod tests {
193228

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

202237
#[test]

0 commit comments

Comments
 (0)