Skip to content

Commit

Permalink
Let c_void be a valid encoding in more places
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Jul 31, 2023
1 parent ce6100c commit 5529dc3
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 5 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
* Allow using `MainThreadMarker` in `extern_methods!`.
* Added the feature flag `"relax-void-encoding"`, which when enabled, allows
using `*mut c_void` in a few places where you would otherwise have to
specify the encoding precisely.

### Changed
* Renamed `runtime` types:
Expand Down
7 changes: 7 additions & 0 deletions crates/objc2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ catch-all = ["exception"]
# Enable all verification steps when debug assertions are enabled.
verify = ["malloc"]

# Allow `*const c_void` and `*mut c_void` to be used as arguments and return
# types where other pointers were expected.
#
# This may be useful for CoreFoundation types, or for migrating code from objc
# to objc2.
relax-void-encoding = []

# Expose features that require linking to `libc::free`.
#
# This is not enabled by default because most users won't need it, and it
Expand Down
62 changes: 57 additions & 5 deletions crates/objc2/src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,22 @@ 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.
///
/// Note: This is a top-level comparison; `*mut *mut c_void` or structures
/// containing `*mut c_void` are not allowed differently than usual.
fn relaxed_equivalent_to_box(encoding: &Encoding, expected: &EncodingBox) -> bool {
if cfg!(feature = "relax-void-encoding")
&& matches!(encoding, Encoding::Pointer(&Encoding::Void))
&& matches!(expected, EncodingBox::Pointer(_))
{
true
} else {
encoding.equivalent_to_box(expected)
}
}

pub(crate) fn verify_method_signature(
method: &Method,
args: &[Encoding],
Expand All @@ -80,7 +96,7 @@ pub(crate) fn verify_method_signature(

// TODO: Verify stack layout
let (expected, _stack_layout) = iter.extract_return()?;
if !ret.equivalent_to_box(&expected) {
if !relaxed_equivalent_to_box(ret, &expected) {
return Err(Inner::MismatchedReturn(expected, ret.clone()).into());
}

Expand All @@ -93,7 +109,7 @@ pub(crate) fn verify_method_signature(
if let Some(res) = iter.next() {
// TODO: Verify stack layout
let (expected, _stack_layout) = res?;
if !actual.equivalent_to_box(&expected) {
if !relaxed_equivalent_to_box(actual, &expected) {
return Err(Inner::MismatchedArgument(i, expected, actual.clone()).into());
}
} else {
Expand All @@ -118,9 +134,10 @@ pub(crate) fn verify_method_signature(
mod tests {
use super::*;
use crate::runtime::Sel;
use crate::sel;
use crate::test_utils;
use crate::{msg_send, sel};
use alloc::string::ToString;
use core::ffi::c_void;
use core::panic::{RefUnwindSafe, UnwindSafe};

#[test]
Expand Down Expand Up @@ -179,20 +196,55 @@ mod tests {
#[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 { crate::msg_send![&obj, foo] };
let _: i32 = unsafe { msg_send![&obj, foo] };
}

#[test]
#[cfg(debug_assertions)]
#[should_panic = "invalid message send to +[CustomObject abcDef]: method not found"]
fn test_send_message_verified_to_class() {
let cls = test_utils::custom_class();
let _: i32 = unsafe { crate::msg_send![cls, abcDef] };
let _: i32 = unsafe { msg_send![cls, abcDef] };
}

#[test]
fn test_marker_traits() {
fn assert_marker_traits<T: Send + Sync + UnwindSafe + RefUnwindSafe + Unpin>() {}
assert_marker_traits::<VerificationError>();
}

#[test]
fn test_get_reference() {
let mut obj = test_utils::custom_object();
let _: () = unsafe { msg_send![&mut obj, setFoo: 42u32] };

let res: &u32 = unsafe { msg_send![&obj, fooReference] };
assert_eq!(*res, 42);
let res: *const u32 = unsafe { msg_send![&obj, fooReference] };
assert_eq!(unsafe { *res }, 42);
let res: *mut u32 = unsafe { msg_send![&obj, fooReference] };
assert_eq!(unsafe { *res }, 42);
}

#[test]
#[cfg_attr(
all(debug_assertions, not(feature = "relax-void-encoding")),
should_panic = "invalid message send to -[CustomObject fooReference]: expected return to have type code '^I', but found '^v'"
)]
fn test_get_reference_void() {
let mut obj = test_utils::custom_object();
let _: () = unsafe { msg_send![&mut obj, setFoo: 42u32] };

let res: *mut c_void = unsafe { msg_send![&obj, fooReference] };
let res: *mut u32 = res.cast();
assert_eq!(unsafe { *res }, 42);
}

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

0 comments on commit 5529dc3

Please sign in to comment.