Skip to content

Commit 155b5c7

Browse files
committed
Added "disable-encoding-assertions" Cargo feature flag
This allows completely disabling message encoding verification, to let users resolve issues such as: - #645 - #671 This is a hack, and ideally we should fix it in some other way (maybe disabling in extern_methods! only?), but for now, this should at least unblock the release.
1 parent d4775a9 commit 155b5c7

File tree

11 files changed

+79
-42
lines changed

11 files changed

+79
-42
lines changed

crates/objc2/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
3939
method family if need be.
4040
* Added `PartialEq`, `Eq`, `Hash`, `PartialOrd` and `Ord` implementations for
4141
`Bool`.
42+
* Added `"disable-encoding-assertions"` Cargo feature flag to allow completely
43+
disabling encoding verification.
4244

4345
### Changed
4446
* **BREAKING**: Renamed `declare_class!` to `define_class!`, and changed the

crates/objc2/Cargo.toml

+15
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,21 @@ relax-void-encoding = []
4848
# instead of `UInt`.
4949
relax-sign-encoding = []
5050

51+
# Fully disable debug assertions on invalid encodings.
52+
#
53+
# Sometimes, the method encoding registered with the runtime doesn't match the
54+
# encoding in the header. This happens for example in NSUUID methods, see:
55+
# https://github.com/madsmtm/objc2/issues/671
56+
#
57+
# Furthermore, sometimes a method may just not be visible to the runtime
58+
# before we actually send a message to it, see:
59+
# https://github.com/madsmtm/objc2/issues/645
60+
#
61+
# Ideally, `objc2`'s encoding checks should not encounter these issues, but
62+
# that is not the state we are in, so for now, this can be used to work around
63+
# them by disabling all encoding checks.
64+
disable-encoding-assertions = []
65+
5166
# Enable deprecation of using `msg_send!` without a comma between arguments.
5267
unstable-msg-send-always-comma = []
5368

crates/objc2/src/__macro_helpers/msg_send_retained.rs

-4
Original file line numberDiff line numberDiff line change
@@ -656,10 +656,6 @@ mod tests {
656656
#[test]
657657
#[should_panic = "tried to initialize ivars after they were already initialized"]
658658
#[cfg_attr(not(debug_assertions), ignore = "only checked with debug assertions")]
659-
#[cfg_attr(
660-
debug_assertions,
661-
ignore = "panicking in `init` requires that we emit the function as `C-unwind`"
662-
)]
663659
fn test_super_init_not_initialized() {
664660
let obj = RcTestObject::alloc().set_ivars(());
665661
let _: Retained<RcTestObject> =

crates/objc2/src/macros/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -937,6 +937,9 @@ macro_rules! __class_inner {
937937
///
938938
/// Panics if `debug_assertions` are enabled and the Objective-C method's
939939
/// encoding does not match the encoding of the given arguments and return.
940+
/// This behaviour can be tweaked with the `"relax-void-encoding"`,
941+
/// `"relax-sign-encoding"` or `"disable-encoding-assertions"` Cargo feature
942+
/// flags if it is causing you trouble.
940943
///
941944
/// Finally, panics if the return type is specified as `Retained<_>`, but the
942945
/// method actually returned NULL. If this happens, you should change the

crates/objc2/src/runtime/define.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ impl ClassBuilder {
282282

283283
// Verify that, if the method is present on the superclass, that the
284284
// encoding is correct.
285-
#[cfg(debug_assertions)]
285+
#[cfg(all(debug_assertions, not(feature = "disable-encoding-assertions")))]
286286
if let Some(superclass) = self.superclass() {
287287
if let Some(method) = superclass.instance_method(sel) {
288288
if let Err(err) = crate::verify::verify_method_signature(method, enc_args, enc_ret)
@@ -347,7 +347,7 @@ impl ClassBuilder {
347347

348348
// Verify that, if the method is present on the superclass, that the
349349
// encoding is correct.
350-
#[cfg(debug_assertions)]
350+
#[cfg(all(debug_assertions, not(feature = "disable-encoding-assertions")))]
351351
if let Some(superclass) = self.superclass() {
352352
if let Some(method) = superclass.class_method(sel) {
353353
if let Err(err) = crate::verify::verify_method_signature(method, enc_args, enc_ret)
@@ -681,7 +681,7 @@ mod tests {
681681

682682
#[test]
683683
#[cfg_attr(
684-
debug_assertions,
684+
all(debug_assertions, not(feature = "disable-encoding-assertions")),
685685
should_panic = "defined invalid method -[TestClassBuilderInvalidMethod foo]: expected return to have type code 'I', but found 's'"
686686
)]
687687
fn invalid_method() {
@@ -699,7 +699,11 @@ mod tests {
699699

700700
#[test]
701701
#[cfg_attr(
702-
all(debug_assertions, not(feature = "relax-sign-encoding")),
702+
all(
703+
debug_assertions,
704+
not(feature = "disable-encoding-assertions"),
705+
not(feature = "relax-sign-encoding")
706+
),
703707
should_panic = "defined invalid method +[TestClassBuilderInvalidClassMethod classFoo]: expected return to have type code 'I', but found 'i'"
704708
)]
705709
fn invalid_class_method() {

crates/objc2/src/runtime/message_receiver.rs

+5
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,11 @@ fn msg_send_check_class(
316316
args: &[crate::encode::Encoding],
317317
ret: &crate::encode::Encoding,
318318
) {
319+
if cfg!(feature = "disable-encoding-assertions") {
320+
// These checks are disabled.
321+
return;
322+
}
323+
319324
use crate::verify::{verify_method_signature, Inner, VerificationError};
320325

321326
let err = if let Some(method) = cls.instance_method(sel) {

crates/objc2/src/runtime/mod.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ impl Ivar {
339339

340340
#[inline]
341341
pub(crate) fn debug_assert_encoding(&self, _expected: &Encoding) {
342-
#[cfg(debug_assertions)]
342+
#[cfg(all(debug_assertions, not(feature = "disable-encoding-assertions")))]
343343
{
344344
let encoding = self.type_encoding();
345345
let encoding = encoding.to_str().expect("encoding must be UTF-8");
@@ -369,7 +369,8 @@ impl Ivar {
369369
/// # Panics
370370
///
371371
/// Panics when `debug_assertions` are enabled if the type encoding of the
372-
/// ivar differs from the type encoding of `T`.
372+
/// ivar differs from the type encoding of `T`. This can be disabled with
373+
/// the `"disable-encoding-assertions"` Cargo feature flag.
373374
///
374375
///
375376
/// # Safety
@@ -407,7 +408,8 @@ impl Ivar {
407408
/// # Panics
408409
///
409410
/// Panics when `debug_assertions` are enabled if the type encoding of the
410-
/// ivar differs from the type encoding of `T`.
411+
/// ivar differs from the type encoding of `T`. This can be disabled with
412+
/// the `"disable-encoding-assertions"` Cargo feature flag.
411413
///
412414
///
413415
/// # Safety
@@ -434,7 +436,8 @@ impl Ivar {
434436
/// # Panics
435437
///
436438
/// Panics when `debug_assertions` are enabled if the type encoding of the
437-
/// ivar differs from the type encoding of `T`.
439+
/// ivar differs from the type encoding of `T`. This can be disabled with
440+
/// the `"disable-encoding-assertions"` Cargo feature flag.
438441
///
439442
///
440443
/// # Safety
@@ -1717,7 +1720,7 @@ mod tests {
17171720

17181721
#[test]
17191722
#[cfg_attr(
1720-
debug_assertions,
1723+
all(debug_assertions, not(feature = "disable-encoding-assertions")),
17211724
should_panic = "wrong encoding. Tried to retrieve ivar with encoding I, but the encoding of the given type was C"
17221725
)]
17231726
fn test_object_ivar_wrong_type() {

crates/objc2/src/verify.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -227,15 +227,15 @@ mod tests {
227227
}
228228

229229
#[test]
230-
#[cfg(debug_assertions)]
230+
#[cfg(all(debug_assertions, not(feature = "disable-encoding-assertions")))]
231231
#[should_panic = "invalid message send to -[CustomObject foo]: expected return to have type code 'I', but found '^i'"]
232232
fn test_send_message_verified() {
233233
let obj = test_utils::custom_object();
234234
let _: *const i32 = unsafe { msg_send![&obj, foo] };
235235
}
236236

237237
#[test]
238-
#[cfg(debug_assertions)]
238+
#[cfg(all(debug_assertions, not(feature = "disable-encoding-assertions")))]
239239
#[should_panic = "invalid message send to +[CustomObject abcDef]: method not found"]
240240
fn test_send_message_verified_to_class() {
241241
let cls = test_utils::custom_class();
@@ -263,7 +263,11 @@ mod tests {
263263

264264
#[test]
265265
#[cfg_attr(
266-
all(debug_assertions, not(feature = "relax-void-encoding")),
266+
all(
267+
debug_assertions,
268+
not(feature = "disable-encoding-assertions"),
269+
not(feature = "relax-void-encoding")
270+
),
267271
should_panic = "invalid message send to -[CustomObject fooReference]: expected return to have type code '^I', but found '^v'"
268272
)]
269273
fn test_get_reference_void() {
@@ -276,7 +280,7 @@ mod tests {
276280
}
277281

278282
#[test]
279-
#[cfg(debug_assertions)]
283+
#[cfg(all(debug_assertions, not(feature = "disable-encoding-assertions")))]
280284
#[should_panic = "invalid message send to -[CustomObject foo]: expected return to have type code 'I', but found '^v'"]
281285
fn test_get_integer_void() {
282286
let obj = test_utils::custom_object();

crates/objc2/tests/track_caller.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -78,17 +78,19 @@ impl Drop for PanicChecker {
7878
fn test_track_caller() {
7979
let checker = PanicChecker::new();
8080

81-
#[cfg(debug_assertions)]
82-
{
81+
if cfg!(debug_assertions) {
8382
test_nil(&checker);
84-
test_verify(&checker);
85-
test_error_methods(&checker);
83+
if cfg!(not(feature = "disable-encoding-assertions")) {
84+
test_verify(&checker);
85+
test_error_methods(&checker);
86+
}
8687
}
8788

8889
test_id_unwrap(&checker);
8990

90-
#[cfg(feature = "catch-all")]
91-
test_catch_all(&checker);
91+
if cfg!(feature = "catch-all") {
92+
test_catch_all(&checker);
93+
}
9294

9395
test_unwind(&checker);
9496

framework-crates/objc2-foundation/src/tests/uuid.rs

+2-8
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,15 @@ fn test_new() {
1919
}
2020

2121
#[test]
22-
#[cfg_attr(
23-
not(target_arch = "aarch64"),
24-
ignore = "encoding depends on Foundation version"
25-
)]
22+
#[ignore = "encoding depends on Foundation version"]
2623
fn test_bytes() {
2724
let uuid = NSUUID::from_bytes([10; 16]);
2825
assert_eq!(uuid.as_bytes(), [10; 16]);
2926
}
3027

3128
#[test]
3229
#[cfg(feature = "NSString")]
33-
#[cfg_attr(
34-
not(target_arch = "aarch64"),
35-
ignore = "encoding depends on Foundation version"
36-
)]
30+
#[ignore = "encoding depends on Foundation version"]
3731
fn display_debug() {
3832
let uuid = NSUUID::from_bytes([10; 16]);
3933
let expected = "0A0A0A0A-0A0A-0A0A-0A0A-0A0A0A0A0A0A";

framework-crates/objc2-foundation/src/uuid.rs

+20-11
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,15 @@ use crate::{util, NSUUID};
1111
impl UnwindSafe for NSUUID {}
1212
impl RefUnwindSafe for NSUUID {}
1313

14-
/// The headers describe `initWithUUIDBytes:` and `getUUIDBytes:` as
15-
/// taking `uuid_t`, but something fishy is going on, in reality they
16-
/// expect a reference to these!
17-
///
18-
/// Hence we create this newtype to change the encoding.
1914
#[repr(transparent)]
2015
struct UuidBytes([u8; 16]);
2116

2217
unsafe impl RefEncode for UuidBytes {
23-
// Encoding depends on Foundation version, we hack it for now.
24-
const ENCODING_REF: Encoding = if cfg!(target_arch = "aarch64") {
25-
Encoding::String
26-
} else {
27-
Encoding::Array(16, &u8::ENCODING)
28-
};
18+
// Encoding actually depends on the instance class, `__NSConcreteUUID` has
19+
// a different method encoding than `NSUUID`, the former takes a `char*`.
20+
//
21+
// This may also depend on the Foundation / OS version.
22+
const ENCODING_REF: Encoding = Encoding::Array(16, &u8::ENCODING);
2923
}
3024

3125
extern_methods!(
@@ -46,6 +40,13 @@ impl NSUUID {
4640

4741
/// Create a new `NSUUID` from the given bytes.
4842
///
43+
/// NOTE: The headers describe `initWithUUIDBytes:` as taking `uuid_t`,
44+
/// but their actual implementation may use something else.
45+
///
46+
/// Thus, to use this method, you must currently disable encoding
47+
/// verification using the `"disable-encoding-assertions"` Cargo feature
48+
/// in `objc2`.
49+
///
4950
///
5051
/// # Example
5152
///
@@ -70,6 +71,14 @@ impl NSUUID {
7071
Self::initWithUUIDString(Self::alloc(), string)
7172
}
7273

74+
/// Convert the UUID to an array.
75+
///
76+
/// NOTE: The headers describe `getUUIDBytes:` as taking `uuid_t`, but
77+
/// their actual implementation may use something else.
78+
///
79+
/// Thus, to use this method, you must currently disable encoding
80+
/// verification using the `"disable-encoding-assertions"` Cargo feature
81+
/// in `objc2`.
7382
pub fn as_bytes(&self) -> [u8; 16] {
7483
let mut bytes = UuidBytes([0; 16]);
7584
self.getUUIDBytes(&mut bytes);

0 commit comments

Comments
 (0)