#[cfg(all(debug_assertions, feature = "verify"))]
use alloc::vec::Vec;
use core::ptr;
#[cfg(all(debug_assertions, feature = "verify"))]
use std::collections::HashSet;
use crate::declare::ClassBuilder;
use crate::declare::MethodImplementation;
use crate::encode::Encode;
use crate::message::__TupleExtender;
use crate::rc::{Allocated, Id};
#[cfg(all(debug_assertions, feature = "verify"))]
use crate::runtime::MethodDescription;
use crate::runtime::{AnyClass, AnyObject, AnyProtocol, Sel};
use crate::{Message, MessageArguments, MessageReceiver};
pub use core::borrow::{Borrow, BorrowMut};
pub use core::cell::UnsafeCell;
pub use core::convert::{AsMut, AsRef};
pub use core::marker::{PhantomData, Sized};
pub use core::mem::{needs_drop, size_of, ManuallyDrop};
pub use core::ops::{Deref, DerefMut};
pub use core::option::Option::{self, None, Some};
pub use core::primitive::{bool, str, u8};
pub use core::ptr::drop_in_place;
pub use core::{compile_error, concat, panic, stringify};
pub use std::sync::Once;
mod cache;
mod common_selectors;
mod declare_class;
pub use self::cache::{CachedClass, CachedSel};
pub use self::common_selectors::{alloc_sel, dealloc_sel, init_sel, new_sel};
pub use self::declare_class::{
assert_mutability_matches_superclass_mutability, MaybeOptionId, MessageRecieveId,
ValidSubclassMutability,
};
pub struct RetainSemantics<const INNER: u8> {}
pub type New = RetainSemantics<1>;
pub type Alloc = RetainSemantics<2>;
pub type Init = RetainSemantics<3>;
pub type CopyOrMutCopy = RetainSemantics<4>;
pub type Other = RetainSemantics<5>;
pub const fn retain_semantics(selector: &str) -> u8 {
let selector = selector.as_bytes();
match (
in_selector_family(selector, b"new"),
in_selector_family(selector, b"alloc"),
in_selector_family(selector, b"init"),
in_selector_family(selector, b"copy"),
in_selector_family(selector, b"mutableCopy"),
) {
(true, false, false, false, false) => 1,
(false, true, false, false, false) => 2,
(false, false, true, false, false) => 3,
(false, false, false, true, false) => 4,
(false, false, false, false, true) => 4,
(false, false, false, false, false) => 5,
_ => unreachable!(),
}
}
pub trait MsgSendId<T, U> {
#[track_caller]
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<Input = U>>(
obj: T,
sel: Sel,
args: A,
) -> R;
#[inline]
#[track_caller]
unsafe fn send_message_id_error<A, E>(obj: T, sel: Sel, args: A) -> Result<U, Id<E>>
where
*mut *mut E: Encode,
A: __TupleExtender<*mut *mut E>,
<A as __TupleExtender<*mut *mut E>>::PlusOneArgument: MessageArguments,
E: Message,
Option<U>: MaybeUnwrap<Input = U>,
{
let mut err: *mut E = ptr::null_mut();
let args = args.add_argument(&mut err);
let res: Option<U> = unsafe { Self::send_message_id(obj, sel, args) };
if let Some(res) = res {
Ok(res)
} else {
Err(unsafe { encountered_error(err) })
}
}
}
#[cold]
#[track_caller]
unsafe fn encountered_error<E: Message>(err: *mut E) -> Id<E> {
unsafe { Id::retain(err) }.expect("error parameter should be set if the method returns NULL")
}
impl<T: MessageReceiver, U: ?Sized + Message> MsgSendId<T, Id<U>> for New {
#[inline]
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<Input = Id<U>>>(
obj: T,
sel: Sel,
args: A,
) -> R {
let ptr = obj.__as_raw_receiver();
let obj = unsafe { MessageReceiver::send_message(ptr, sel, args) };
let obj = unsafe { Id::new(obj) };
R::maybe_unwrap::<Self>(obj, (unsafe { ptr.as_ref() }, sel))
}
}
impl<T: ?Sized + Message> MsgSendId<&'_ AnyClass, Allocated<T>> for Alloc {
#[inline]
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<Input = Allocated<T>>>(
cls: &AnyClass,
sel: Sel,
args: A,
) -> R {
let obj = unsafe { MessageReceiver::send_message(cls, sel, args) };
let obj = unsafe { Allocated::new(obj) };
R::maybe_unwrap::<Self>(obj, (cls, sel))
}
}
impl<T: ?Sized + Message> MsgSendId<Option<Allocated<T>>, Id<T>> for Init {
#[inline]
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<Input = Id<T>>>(
obj: Option<Allocated<T>>,
sel: Sel,
args: A,
) -> R {
let ptr = Allocated::option_into_ptr(obj);
let obj = unsafe { MessageReceiver::send_message(ptr, sel, args) };
let obj = unsafe { Id::new(obj) };
R::maybe_unwrap::<Self>(obj, (ptr.cast(), sel))
}
}
impl<T: MessageReceiver, U: ?Sized + Message> MsgSendId<T, Id<U>> for CopyOrMutCopy {
#[inline]
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<Input = Id<U>>>(
obj: T,
sel: Sel,
args: A,
) -> R {
let obj = unsafe { MessageReceiver::send_message(obj, sel, args) };
let obj = unsafe { Id::new(obj) };
R::maybe_unwrap::<Self>(obj, ())
}
}
impl<T: MessageReceiver, U: Message> MsgSendId<T, Id<U>> for Other {
#[inline]
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<Input = Id<U>>>(
obj: T,
sel: Sel,
args: A,
) -> R {
let ptr = obj.__as_raw_receiver();
let obj = unsafe { MessageReceiver::send_message(ptr, sel, args) };
let obj = unsafe { Id::retain_autoreleased(obj) };
R::maybe_unwrap::<Self>(obj, (unsafe { ptr.as_ref() }, sel))
}
}
pub trait MaybeUnwrap {
type Input;
#[track_caller]
fn maybe_unwrap<'a, F: MsgSendIdFailed<'a>>(obj: Option<Self::Input>, args: F::Args) -> Self;
}
impl<T: ?Sized> MaybeUnwrap for Option<Id<T>> {
type Input = Id<T>;
#[inline]
fn maybe_unwrap<'a, F: MsgSendIdFailed<'a>>(obj: Option<Id<T>>, _args: F::Args) -> Self {
obj
}
}
impl<T: ?Sized> MaybeUnwrap for Id<T> {
type Input = Id<T>;
#[inline]
fn maybe_unwrap<'a, F: MsgSendIdFailed<'a>>(obj: Option<Id<T>>, args: F::Args) -> Self {
match obj {
Some(obj) => obj,
None => F::failed(args),
}
}
}
impl<T: ?Sized> MaybeUnwrap for Option<Allocated<T>> {
type Input = Allocated<T>;
#[inline]
fn maybe_unwrap<'a, F: MsgSendIdFailed<'a>>(obj: Option<Allocated<T>>, _args: F::Args) -> Self {
obj
}
}
impl<T: ?Sized> MaybeUnwrap for Allocated<T> {
type Input = Allocated<T>;
#[inline]
fn maybe_unwrap<'a, F: MsgSendIdFailed<'a>>(obj: Option<Allocated<T>>, args: F::Args) -> Self {
match obj {
Some(obj) => obj,
None => F::failed(args),
}
}
}
pub trait MsgSendIdFailed<'a> {
type Args;
#[track_caller]
fn failed(args: Self::Args) -> !;
}
impl<'a> MsgSendIdFailed<'a> for New {
type Args = (Option<&'a AnyObject>, Sel);
#[cold]
fn failed((obj, sel): Self::Args) -> ! {
if let Some(obj) = obj {
let cls = obj.class();
if cls.is_metaclass() {
if sel == new_sel() {
panic!("failed creating new instance of {cls}")
} else {
panic!("failed creating new instance using +[{cls} {sel}]")
}
} else {
panic!("unexpected NULL returned from -[{cls} {sel}]")
}
} else {
panic!("unexpected NULL {sel}; receiver was NULL");
}
}
}
impl<'a> MsgSendIdFailed<'a> for Alloc {
type Args = (&'a AnyClass, Sel);
#[cold]
fn failed((cls, sel): Self::Args) -> ! {
if sel == alloc_sel() {
panic!("failed allocating {cls}")
} else {
panic!("failed allocating with +[{cls} {sel}]")
}
}
}
impl MsgSendIdFailed<'_> for Init {
type Args = (*const AnyObject, Sel);
#[cold]
fn failed((ptr, sel): Self::Args) -> ! {
if ptr.is_null() {
panic!("failed allocating object")
} else {
if sel == init_sel() {
panic!("failed initializing object")
} else {
panic!("failed initializing object with -{sel}")
}
}
}
}
impl MsgSendIdFailed<'_> for CopyOrMutCopy {
type Args = ();
#[cold]
fn failed(_: Self::Args) -> ! {
panic!("failed copying object")
}
}
impl<'a> MsgSendIdFailed<'a> for Other {
type Args = (Option<&'a AnyObject>, Sel);
#[cold]
fn failed((obj, sel): Self::Args) -> ! {
if let Some(obj) = obj {
let cls = obj.class();
panic!(
"unexpected NULL returned from {}[{cls} {sel}]",
if cls.is_metaclass() { "+" } else { "-" },
)
} else {
panic!("unexpected NULL {sel}; receiver was NULL");
}
}
}
const fn in_selector_family(mut selector: &[u8], mut family: &[u8]) -> bool {
loop {
selector = match selector {
[b'_', rest @ ..] => rest,
_ => break,
}
}
loop {
(selector, family) = match (selector, family) {
([s, selector @ ..], [f, family @ ..]) => {
if *s == *f {
(selector, family)
} else {
return false;
}
}
([], []) => {
return true;
}
([], _) => {
return false;
}
([s, ..], []) => {
return !s.is_ascii_lowercase();
}
}
}
}
#[repr(C)]
pub struct ModuleInfo {
version: usize,
size: usize,
name: *const u8,
symtab: *const (),
}
unsafe impl Sync for ModuleInfo {}
impl ModuleInfo {
const VERSION: usize = 7;
pub const fn new(name: *const u8) -> Self {
Self {
version: Self::VERSION,
size: core::mem::size_of::<Self>(),
name,
symtab: core::ptr::null(),
}
}
}
impl ClassBuilder {
#[doc(hidden)]
pub fn __add_protocol_methods<'a, 'b>(
&'a mut self,
protocol: Option<&'b AnyProtocol>,
) -> ClassProtocolMethodsBuilder<'a, 'b> {
if let Some(protocol) = protocol {
self.add_protocol(protocol);
}
#[cfg(all(debug_assertions, feature = "verify"))]
{
ClassProtocolMethodsBuilder {
builder: self,
protocol,
required_instance_methods: protocol
.map(|p| p.method_descriptions(true))
.unwrap_or_default(),
optional_instance_methods: protocol
.map(|p| p.method_descriptions(false))
.unwrap_or_default(),
registered_instance_methods: HashSet::new(),
required_class_methods: protocol
.map(|p| p.class_method_descriptions(true))
.unwrap_or_default(),
optional_class_methods: protocol
.map(|p| p.class_method_descriptions(false))
.unwrap_or_default(),
registered_class_methods: HashSet::new(),
}
}
#[cfg(not(all(debug_assertions, feature = "verify")))]
{
ClassProtocolMethodsBuilder {
builder: self,
protocol,
}
}
}
}
pub struct ClassProtocolMethodsBuilder<'a, 'b> {
builder: &'a mut ClassBuilder,
#[allow(unused)]
protocol: Option<&'b AnyProtocol>,
#[cfg(all(debug_assertions, feature = "verify"))]
required_instance_methods: Vec<MethodDescription>,
#[cfg(all(debug_assertions, feature = "verify"))]
optional_instance_methods: Vec<MethodDescription>,
#[cfg(all(debug_assertions, feature = "verify"))]
registered_instance_methods: HashSet<Sel>,
#[cfg(all(debug_assertions, feature = "verify"))]
required_class_methods: Vec<MethodDescription>,
#[cfg(all(debug_assertions, feature = "verify"))]
optional_class_methods: Vec<MethodDescription>,
#[cfg(all(debug_assertions, feature = "verify"))]
registered_class_methods: HashSet<Sel>,
}
impl ClassProtocolMethodsBuilder<'_, '_> {
#[inline]
pub unsafe fn add_method<T, F>(&mut self, sel: Sel, func: F)
where
T: Message + ?Sized,
F: MethodImplementation<Callee = T>,
{
#[cfg(all(debug_assertions, feature = "verify"))]
if let Some(protocol) = self.protocol {
let _types = self
.required_instance_methods
.iter()
.chain(&self.optional_instance_methods)
.find(|desc| desc.sel == sel)
.map(|desc| desc.types)
.unwrap_or_else(|| {
panic!(
"failed overriding protocol method -[{protocol} {sel}]: method not found"
)
});
}
unsafe { self.builder.add_method(sel, func) };
#[cfg(all(debug_assertions, feature = "verify"))]
if !self.registered_instance_methods.insert(sel) {
unreachable!("already added")
}
}
#[inline]
pub unsafe fn add_class_method<F>(&mut self, sel: Sel, func: F)
where
F: MethodImplementation<Callee = AnyClass>,
{
#[cfg(all(debug_assertions, feature = "verify"))]
if let Some(protocol) = self.protocol {
let _types = self
.required_class_methods
.iter()
.chain(&self.optional_class_methods)
.find(|desc| desc.sel == sel)
.map(|desc| desc.types)
.unwrap_or_else(|| {
panic!(
"failed overriding protocol method +[{protocol} {sel}]: method not found"
)
});
}
unsafe { self.builder.add_class_method(sel, func) };
#[cfg(all(debug_assertions, feature = "verify"))]
if !self.registered_class_methods.insert(sel) {
unreachable!("already added")
}
}
#[inline]
pub fn __finish(self) {
#[cfg(all(debug_assertions, feature = "verify"))]
if let Some(protocol) = self.protocol {
for desc in &self.required_instance_methods {
if !self.registered_instance_methods.contains(&desc.sel) {
panic!(
"must implement required protocol method -[{protocol} {}]",
desc.sel
)
}
}
}
#[cfg(all(debug_assertions, feature = "verify"))]
if let Some(protocol) = self.protocol {
for desc in &self.required_class_methods {
if !self.registered_class_methods.contains(&desc.sel) {
panic!(
"must implement required protocol method +[{protocol} {}]",
desc.sel
)
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::string::ToString;
use core::ptr;
#[cfg(feature = "objc2-proc-macros")]
use crate::__hash_idents;
use crate::rc::{__RcTestObject, __ThreadTestData};
use crate::runtime::{AnyObject, NSObject, NSZone};
use crate::{class, msg_send_id, ClassType};
#[test]
fn test_new() {
let _obj: Id<AnyObject> = unsafe { msg_send_id![NSObject::class(), new] };
let _obj: Option<Id<AnyObject>> = unsafe { msg_send_id![NSObject::class(), new] };
}
#[test]
fn test_new_not_on_class() {
let mut expected = __ThreadTestData::current();
let obj = __RcTestObject::new();
let _obj: Id<AnyObject> = unsafe { msg_send_id![&obj, newMethodOnInstance] };
let _obj: Option<Id<AnyObject>> = unsafe { msg_send_id![&obj, newMethodOnInstance] };
expected.alloc += 3;
expected.init += 3;
expected.assert_current();
}
#[test]
#[cfg_attr(not(all(feature = "apple", target_os = "macos")), ignore)]
fn test_new_with_args() {
let mut expected = __ThreadTestData::current();
let object_class = __RcTestObject::class();
let key: Id<AnyObject> = unsafe { msg_send_id![class!(NSString), new] };
let contents_value: *const AnyObject = ptr::null();
let properties: Id<AnyObject> = unsafe { msg_send_id![class!(NSDictionary), new] };
let _obj: Option<Id<AnyObject>> = unsafe {
msg_send_id![
NSObject::class(),
newScriptingObjectOfClass: object_class,
forValueForKey: &*key,
withContentsValue: contents_value,
properties: &*properties,
]
};
expected.alloc += 1;
expected.init += 1;
expected.assert_current();
}
#[test]
fn test_macro_alloc() {
let mut expected = __ThreadTestData::current();
let cls = __RcTestObject::class();
let obj: Allocated<__RcTestObject> = unsafe { msg_send_id![cls, alloc] };
expected.alloc += 1;
expected.assert_current();
drop(obj);
expected.release += 1;
expected.dealloc += 1;
expected.assert_current();
}
#[test]
fn test_alloc_with_zone() {
let mut expected = __ThreadTestData::current();
let cls = __RcTestObject::class();
let zone: *const NSZone = ptr::null();
let _obj: Allocated<__RcTestObject> = unsafe { msg_send_id![cls, allocWithZone: zone] };
expected.alloc += 1;
expected.assert_current();
}
#[test]
fn test_macro_init() {
let mut expected = __ThreadTestData::current();
let cls = __RcTestObject::class();
let obj: Option<Allocated<__RcTestObject>> = unsafe { msg_send_id![cls, alloc] };
expected.alloc += 1;
let _obj: Id<__RcTestObject> = unsafe { msg_send_id![obj, init] };
expected.init += 1;
expected.assert_current();
let obj: Option<Allocated<__RcTestObject>> = unsafe { msg_send_id![cls, alloc] };
expected.alloc += 1;
let obj = obj.unwrap();
let _obj: Id<__RcTestObject> = unsafe { msg_send_id![Some(obj), init] };
expected.init += 1;
expected.assert_current();
}
#[test]
fn test_macro() {
let mut expected = __ThreadTestData::current();
let cls = __RcTestObject::class();
crate::rc::autoreleasepool(|_| {
let _obj: Id<__RcTestObject> = unsafe { msg_send_id![cls, new] };
expected.alloc += 1;
expected.init += 1;
expected.assert_current();
let obj = unsafe { msg_send_id![cls, alloc] };
expected.alloc += 1;
expected.assert_current();
let obj: Id<__RcTestObject> = unsafe { msg_send_id![obj, init] };
expected.init += 1;
expected.assert_current();
let _copy: Id<__RcTestObject> = unsafe { msg_send_id![&obj, copy] };
expected.copy += 1;
expected.alloc += 1;
expected.init += 1;
expected.assert_current();
let _mutable_copy: Id<__RcTestObject> = unsafe { msg_send_id![&obj, mutableCopy] };
expected.mutable_copy += 1;
expected.alloc += 1;
expected.init += 1;
expected.assert_current();
let _self: Id<__RcTestObject> = unsafe { msg_send_id![&obj, self] };
expected.retain += 1;
expected.assert_current();
let _desc: Option<Id<__RcTestObject>> = unsafe { msg_send_id![&obj, description] };
expected.assert_current();
});
expected.release += 5;
expected.dealloc += 4;
expected.assert_current();
}
#[test]
#[should_panic = "failed creating new instance of NSValue"]
#[cfg_attr(feature = "gnustep-1-7", ignore)]
fn new_nsvalue_fails() {
let _val: Id<AnyObject> = unsafe { msg_send_id![class!(NSValue), new] };
}
#[test]
#[should_panic = "failed creating new instance using +[__RcTestObject newReturningNull]"]
fn test_new_with_null() {
let _obj: Id<__RcTestObject> =
unsafe { msg_send_id![__RcTestObject::class(), newReturningNull] };
}
#[test]
#[should_panic = "unexpected NULL returned from -[__RcTestObject newMethodOnInstanceNull]"]
fn test_new_any_with_null() {
let obj = __RcTestObject::new();
let _obj: Id<AnyObject> = unsafe { msg_send_id![&obj, newMethodOnInstanceNull] };
}
#[test]
#[should_panic = "unexpected NULL newMethodOnInstance; receiver was NULL"]
#[cfg(not(debug_assertions))] fn test_new_any_with_null_receiver() {
let obj: *const NSObject = ptr::null();
let _obj: Id<AnyObject> = unsafe { msg_send_id![obj, newMethodOnInstance] };
}
#[test]
#[should_panic = "failed allocating with +[__RcTestObject allocReturningNull]"]
fn test_alloc_with_null() {
let _obj: Allocated<__RcTestObject> =
unsafe { msg_send_id![__RcTestObject::class(), allocReturningNull] };
}
#[test]
#[should_panic = "failed initializing object with -initReturningNull"]
fn test_init_with_null() {
let obj: Option<Allocated<__RcTestObject>> =
unsafe { msg_send_id![__RcTestObject::class(), alloc] };
let _obj: Id<__RcTestObject> = unsafe { msg_send_id![obj, initReturningNull] };
}
#[test]
#[should_panic = "failed allocating object"]
#[cfg(not(debug_assertions))] fn test_init_with_null_receiver() {
let obj: Option<Allocated<__RcTestObject>> = None;
let _obj: Id<__RcTestObject> = unsafe { msg_send_id![obj, init] };
}
#[test]
#[should_panic = "failed copying object"]
fn test_copy_with_null() {
let obj = __RcTestObject::new();
let _obj: Id<__RcTestObject> = unsafe { msg_send_id![&obj, copyReturningNull] };
}
#[test]
#[should_panic = "unexpected NULL returned from -[__RcTestObject methodReturningNull]"]
fn test_normal_with_null() {
let obj = __RcTestObject::new();
let _obj: Id<__RcTestObject> = unsafe { msg_send_id![&obj, methodReturningNull] };
}
#[test]
#[should_panic = "unexpected NULL returned from -[__RcTestObject aMethod:]"]
fn test_normal_with_param_and_null() {
let obj = __RcTestObject::new();
let _obj: Id<__RcTestObject> = unsafe { msg_send_id![&obj, aMethod: false] };
}
#[test]
#[should_panic = "unexpected NULL description; receiver was NULL"]
#[cfg(not(debug_assertions))] fn test_normal_with_null_receiver() {
let obj: *const NSObject = ptr::null();
let _obj: Id<AnyObject> = unsafe { msg_send_id![obj, description] };
}
#[test]
fn test_in_selector_family() {
#[track_caller]
fn assert_in_family(selector: &str, family: &str) {
assert!(in_selector_family(selector.as_bytes(), family.as_bytes()));
let selector = selector.to_string() + "\0";
assert!(in_selector_family(selector.as_bytes(), family.as_bytes()));
}
#[track_caller]
fn assert_not_in_family(selector: &str, family: &str) {
assert!(!in_selector_family(selector.as_bytes(), family.as_bytes()));
let selector = selector.to_string() + "\0";
assert!(!in_selector_family(selector.as_bytes(), family.as_bytes()));
}
assert_in_family("alloc", "alloc");
assert_in_family("allocWithZone:", "alloc");
assert_not_in_family("dealloc", "alloc");
assert_not_in_family("initialize", "init");
assert_not_in_family("decimalNumberWithDecimal:", "init");
assert_in_family("initWithCapacity:", "init");
assert_in_family("_initButPrivate:withParam:", "init");
assert_not_in_family("description", "init");
assert_not_in_family("inIT", "init");
assert_not_in_family("init", "copy");
assert_not_in_family("copyingStuff:", "copy");
assert_in_family("copyWithZone:", "copy");
assert_not_in_family("initWithArray:copyItems:", "copy");
assert_in_family("copyItemAtURL:toURL:error:", "copy");
assert_not_in_family("mutableCopying", "mutableCopy");
assert_in_family("mutableCopyWithZone:", "mutableCopy");
assert_in_family("mutableCopyWithZone:", "mutableCopy");
assert_in_family(
"newScriptingObjectOfClass:forValueForKey:withContentsValue:properties:",
"new",
);
assert_in_family(
"newScriptingObjectOfClass:forValueForKey:withContentsValue:properties:",
"new",
);
assert_not_in_family("newsstandAssetDownload", "new");
assert_in_family("__abcDef", "abc");
assert_in_family("_abcDef", "abc");
assert_in_family("abcDef", "abc");
assert_in_family("___a", "a");
assert_in_family("__a", "a");
assert_in_family("_a", "a");
assert_in_family("a", "a");
assert_not_in_family("_abcdef", "abc");
assert_not_in_family("_abcdef", "def");
assert_not_in_family("_bcdef", "abc");
assert_not_in_family("a_bc", "abc");
assert_not_in_family("abcdef", "abc");
assert_not_in_family("abcdef", "def");
assert_not_in_family("abcdef", "abb");
assert_not_in_family("___", "a");
assert_not_in_family("_", "a");
assert_not_in_family("", "a");
assert_in_family("copy", "copy");
assert_in_family("copy:", "copy");
assert_in_family("copyMe", "copy");
assert_in_family("_copy", "copy");
assert_in_family("_copy:", "copy");
assert_in_family("_copyMe", "copy");
assert_not_in_family("copying", "copy");
assert_not_in_family("copying:", "copy");
assert_not_in_family("_copying", "copy");
assert_not_in_family("Copy", "copy");
assert_not_in_family("COPY", "copy");
assert_in_family("___", "");
assert_in_family("__", "");
assert_in_family("_", "");
assert_in_family("", "");
assert_not_in_family("_a", "");
assert_not_in_family("a", "");
assert_in_family("_A", "");
assert_in_family("A", "");
assert_in_family("abc::abc::", "abc");
assert_in_family("abc:::", "abc");
assert_in_family("abcDef::xyz:", "abc");
assert_not_in_family("::abc:", "abc");
}
mod test_trait_disambugated {
use super::*;
trait Abc {
fn send_message_id() {}
}
impl<T> Abc for T {}
#[test]
fn test_macro_still_works() {
let cls = class!(NSObject);
let _obj: Id<AnyObject> = unsafe { msg_send_id![cls, new] };
}
}
#[test]
#[cfg(feature = "objc2-proc-macros")]
fn hash_idents_different() {
assert_ne!(__hash_idents!(abc), __hash_idents!(def));
}
#[test]
#[cfg(feature = "objc2-proc-macros")]
fn hash_idents_same_no_equal() {
assert_ne!(__hash_idents!(abc), __hash_idents!(abc));
assert_ne!(__hash_idents!(abc def ghi), __hash_idents!(abc def ghi));
}
#[test]
#[cfg(feature = "objc2-proc-macros")]
fn hash_idents_exact_same_ident() {
macro_rules! x {
($x:ident) => {
(__hash_idents!($x), __hash_idents!($x))
};
}
let (ident1, ident2) = x!(abc);
assert_eq!(ident1, ident2);
}
#[test]
#[cfg_attr(
not(all(feature = "apple", target_os = "macos", target_arch = "x86")),
ignore = "Only relevant on macOS 32-bit"
)]
fn ensure_size_of_module_info() {
assert_eq!(core::mem::size_of::<ModuleInfo>(), 16);
}
}