From 5529dc3b68cdfaa6ecbec827a39019a4cb3d3526 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 31 Jul 2023 16:39:11 +0200 Subject: [PATCH] Let c_void be a valid encoding in more places --- crates/objc2/CHANGELOG.md | 3 ++ crates/objc2/Cargo.toml | 7 +++++ crates/objc2/src/verify.rs | 62 +++++++++++++++++++++++++++++++++++--- 3 files changed, 67 insertions(+), 5 deletions(-) diff --git a/crates/objc2/CHANGELOG.md b/crates/objc2/CHANGELOG.md index 8606553af..f38685187 100644 --- a/crates/objc2/CHANGELOG.md +++ b/crates/objc2/CHANGELOG.md @@ -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: diff --git a/crates/objc2/Cargo.toml b/crates/objc2/Cargo.toml index 0c96b2324..5ecad87b7 100644 --- a/crates/objc2/Cargo.toml +++ b/crates/objc2/Cargo.toml @@ -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 diff --git a/crates/objc2/src/verify.rs b/crates/objc2/src/verify.rs index 2c3963f61..6aad8dc3c 100644 --- a/crates/objc2/src/verify.rs +++ b/crates/objc2/src/verify.rs @@ -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], @@ -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()); } @@ -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 { @@ -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] @@ -179,7 +196,7 @@ 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] @@ -187,7 +204,7 @@ mod tests { #[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] @@ -195,4 +212,39 @@ mod tests { fn assert_marker_traits() {} assert_marker_traits::(); } + + #[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] }; + } }