summaryrefslogtreecommitdiffstats
path: root/third_party/rust/objc/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/objc/src
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/objc/src')
-rw-r--r--third_party/rust/objc/src/declare.rs340
-rw-r--r--third_party/rust/objc/src/encode.rs279
-rw-r--r--third_party/rust/objc/src/exception.rs11
-rw-r--r--third_party/rust/objc/src/lib.rs90
-rw-r--r--third_party/rust/objc/src/macros.rs148
-rw-r--r--third_party/rust/objc/src/message/apple/arm.rs40
-rw-r--r--third_party/rust/objc/src/message/apple/arm64.rs18
-rw-r--r--third_party/rust/objc/src/message/apple/mod.rs40
-rw-r--r--third_party/rust/objc/src/message/apple/x86.rs40
-rw-r--r--third_party/rust/objc/src/message/apple/x86_64.rs32
-rw-r--r--third_party/rust/objc/src/message/gnustep.rs35
-rw-r--r--third_party/rust/objc/src/message/mod.rs296
-rw-r--r--third_party/rust/objc/src/message/verify.rs49
-rw-r--r--third_party/rust/objc/src/rc/autorelease.rs30
-rw-r--r--third_party/rust/objc/src/rc/mod.rs123
-rw-r--r--third_party/rust/objc/src/rc/strong.rs73
-rw-r--r--third_party/rust/objc/src/rc/weak.rs50
-rw-r--r--third_party/rust/objc/src/runtime.rs632
-rw-r--r--third_party/rust/objc/src/test_utils.rs187
19 files changed, 2513 insertions, 0 deletions
diff --git a/third_party/rust/objc/src/declare.rs b/third_party/rust/objc/src/declare.rs
new file mode 100644
index 0000000000..ed09015e1a
--- /dev/null
+++ b/third_party/rust/objc/src/declare.rs
@@ -0,0 +1,340 @@
+/*!
+Functionality for declaring Objective-C classes.
+
+Classes can be declared using the `ClassDecl` struct. Instance variables and
+methods can then be added before the class is ultimately registered.
+
+# Example
+
+The following example demonstrates declaring a class named `MyNumber` that has
+one ivar, a `u32` named `_number` and a `number` method that returns it:
+
+``` no_run
+# #[macro_use] extern crate objc;
+# use objc::declare::ClassDecl;
+# use objc::runtime::{Class, Object, Sel};
+# fn main() {
+let superclass = class!(NSObject);
+let mut decl = ClassDecl::new("MyNumber", superclass).unwrap();
+
+// Add an instance variable
+decl.add_ivar::<u32>("_number");
+
+// Add an ObjC method for getting the number
+extern fn my_number_get(this: &Object, _cmd: Sel) -> u32 {
+ unsafe { *this.get_ivar("_number") }
+}
+unsafe {
+ decl.add_method(sel!(number),
+ my_number_get as extern fn(&Object, Sel) -> u32);
+}
+
+decl.register();
+# }
+```
+*/
+
+use std::ffi::CString;
+use std::mem;
+use std::ptr;
+
+use runtime::{BOOL, Class, Imp, NO, Object, Protocol, Sel, self};
+use {Encode, EncodeArguments, Encoding, Message};
+
+/// Types that can be used as the implementation of an Objective-C method.
+pub trait MethodImplementation {
+ /// The callee type of the method.
+ type Callee: Message;
+ /// The return type of the method.
+ type Ret: Encode;
+ /// The argument types of the method.
+ type Args: EncodeArguments;
+
+ /// Returns self as an `Imp` of a method.
+ fn imp(self) -> Imp;
+}
+
+macro_rules! method_decl_impl {
+ (-$s:ident, $r:ident, $f:ty, $($t:ident),*) => (
+ impl<$s, $r $(, $t)*> MethodImplementation for $f
+ where $s: Message, $r: Encode $(, $t: Encode)* {
+ type Callee = $s;
+ type Ret = $r;
+ type Args = ($($t,)*);
+
+ fn imp(self) -> Imp {
+ unsafe { mem::transmute(self) }
+ }
+ }
+ );
+ ($($t:ident),*) => (
+ method_decl_impl!(-T, R, extern fn(&T, Sel $(, $t)*) -> R, $($t),*);
+ method_decl_impl!(-T, R, extern fn(&mut T, Sel $(, $t)*) -> R, $($t),*);
+ );
+}
+
+method_decl_impl!();
+method_decl_impl!(A);
+method_decl_impl!(A, B);
+method_decl_impl!(A, B, C);
+method_decl_impl!(A, B, C, D);
+method_decl_impl!(A, B, C, D, E);
+method_decl_impl!(A, B, C, D, E, F);
+method_decl_impl!(A, B, C, D, E, F, G);
+method_decl_impl!(A, B, C, D, E, F, G, H);
+method_decl_impl!(A, B, C, D, E, F, G, H, I);
+method_decl_impl!(A, B, C, D, E, F, G, H, I, J);
+method_decl_impl!(A, B, C, D, E, F, G, H, I, J, K);
+method_decl_impl!(A, B, C, D, E, F, G, H, I, J, K, L);
+
+fn count_args(sel: Sel) -> usize {
+ sel.name().chars().filter(|&c| c == ':').count()
+}
+
+fn method_type_encoding(ret: &Encoding, args: &[Encoding]) -> CString {
+ let mut types = ret.as_str().to_owned();
+ // First two arguments are always self and the selector
+ types.push_str(<*mut Object>::encode().as_str());
+ types.push_str(Sel::encode().as_str());
+ types.extend(args.iter().map(|e| e.as_str()));
+ CString::new(types).unwrap()
+}
+
+fn log2_align_of<T>() -> u8 {
+ let align = mem::align_of::<T>();
+ // Alignments are required to be powers of 2
+ debug_assert!(align.count_ones() == 1);
+ // log2 of a power of 2 is the number of trailing zeros
+ align.trailing_zeros() as u8
+}
+
+/// A type for declaring a new class and adding new methods and ivars to it
+/// before registering it.
+pub struct ClassDecl {
+ cls: *mut Class,
+}
+
+impl ClassDecl {
+ fn with_superclass(name: &str, superclass: Option<&Class>)
+ -> Option<ClassDecl> {
+ let name = CString::new(name).unwrap();
+ let super_ptr = superclass.map_or(ptr::null(), |c| c);
+ let cls = unsafe {
+ runtime::objc_allocateClassPair(super_ptr, name.as_ptr(), 0)
+ };
+ if cls.is_null() {
+ None
+ } else {
+ Some(ClassDecl { cls: cls })
+ }
+ }
+
+ /// Constructs a `ClassDecl` with the given name and superclass.
+ /// Returns `None` if the class couldn't be allocated.
+ pub fn new(name: &str, superclass: &Class) -> Option<ClassDecl> {
+ ClassDecl::with_superclass(name, Some(superclass))
+ }
+
+ /**
+ Constructs a `ClassDecl` declaring a new root class with the given name.
+ Returns `None` if the class couldn't be allocated.
+
+ An implementation for `+initialize` must also be given; the runtime calls
+ this method for all classes, so it must be defined on root classes.
+
+ Note that implementing a root class is not a simple endeavor.
+ For example, your class probably cannot be passed to Cocoa code unless
+ the entire `NSObject` protocol is implemented.
+ Functionality it expects, like implementations of `-retain` and `-release`
+ used by ARC, will not be present otherwise.
+ */
+ pub fn root(name: &str, intitialize_fn: extern fn(&Class, Sel))
+ -> Option<ClassDecl> {
+ let mut decl = ClassDecl::with_superclass(name, None);
+ if let Some(ref mut decl) = decl {
+ unsafe {
+ decl.add_class_method(sel!(initialize), intitialize_fn);
+ }
+ }
+ decl
+ }
+
+ /// Adds a method with the given name and implementation to self.
+ /// Panics if the method wasn't sucessfully added
+ /// or if the selector and function take different numbers of arguments.
+ /// Unsafe because the caller must ensure that the types match those that
+ /// are expected when the method is invoked from Objective-C.
+ pub unsafe fn add_method<F>(&mut self, sel: Sel, func: F)
+ where F: MethodImplementation<Callee=Object> {
+ let encs = F::Args::encodings();
+ let encs = encs.as_ref();
+ let sel_args = count_args(sel);
+ assert!(sel_args == encs.len(),
+ "Selector accepts {} arguments, but function accepts {}",
+ sel_args, encs.len(),
+ );
+
+ let types = method_type_encoding(&F::Ret::encode(), encs);
+ let success = runtime::class_addMethod(self.cls, sel, func.imp(),
+ types.as_ptr());
+ assert!(success != NO, "Failed to add method {:?}", sel);
+ }
+
+ /// Adds a class method with the given name and implementation to self.
+ /// Panics if the method wasn't sucessfully added
+ /// or if the selector and function take different numbers of arguments.
+ /// Unsafe because the caller must ensure that the types match those that
+ /// are expected when the method is invoked from Objective-C.
+ pub unsafe fn add_class_method<F>(&mut self, sel: Sel, func: F)
+ where F: MethodImplementation<Callee=Class> {
+ let encs = F::Args::encodings();
+ let encs = encs.as_ref();
+ let sel_args = count_args(sel);
+ assert!(sel_args == encs.len(),
+ "Selector accepts {} arguments, but function accepts {}",
+ sel_args, encs.len(),
+ );
+
+ let types = method_type_encoding(&F::Ret::encode(), encs);
+ let metaclass = (*self.cls).metaclass() as *const _ as *mut _;
+ let success = runtime::class_addMethod(metaclass, sel, func.imp(),
+ types.as_ptr());
+ assert!(success != NO, "Failed to add class method {:?}", sel);
+ }
+
+ /// Adds an ivar with type `T` and the provided name to self.
+ /// Panics if the ivar wasn't successfully added.
+ pub fn add_ivar<T>(&mut self, name: &str) where T: Encode {
+ let c_name = CString::new(name).unwrap();
+ let encoding = CString::new(T::encode().as_str()).unwrap();
+ let size = mem::size_of::<T>();
+ let align = log2_align_of::<T>();
+ let success = unsafe {
+ runtime::class_addIvar(self.cls, c_name.as_ptr(), size, align,
+ encoding.as_ptr())
+ };
+ assert!(success != NO, "Failed to add ivar {}", name);
+ }
+
+ /// Adds a protocol to self. Panics if the protocol wasn't successfully
+ /// added
+ pub fn add_protocol(&mut self, proto: &Protocol) {
+ let success = unsafe { runtime::class_addProtocol(self.cls, proto) };
+ assert!(success != NO, "Failed to add protocol {:?}", proto);
+ }
+
+ /// Registers self, consuming it and returning a reference to the
+ /// newly registered `Class`.
+ pub fn register(self) -> &'static Class {
+ unsafe {
+ let cls = self.cls;
+ runtime::objc_registerClassPair(cls);
+ // Forget self otherwise the class will be disposed in drop
+ mem::forget(self);
+ &*cls
+ }
+ }
+}
+
+impl Drop for ClassDecl {
+ fn drop(&mut self) {
+ unsafe {
+ runtime::objc_disposeClassPair(self.cls);
+ }
+ }
+}
+
+/// A type for declaring a new protocol and adding new methods to it
+/// before registering it.
+pub struct ProtocolDecl {
+ proto: *mut Protocol
+}
+
+impl ProtocolDecl {
+ /// Constructs a `ProtocolDecl` with the given name. Returns `None` if the
+ /// protocol couldn't be allocated.
+ pub fn new(name: &str) -> Option<ProtocolDecl> {
+ let c_name = CString::new(name).unwrap();
+ let proto = unsafe {
+ runtime::objc_allocateProtocol(c_name.as_ptr())
+ };
+ if proto.is_null() {
+ None
+ } else {
+ Some(ProtocolDecl { proto: proto })
+ }
+ }
+
+ fn add_method_description_common<Args, Ret>(&mut self, sel: Sel, is_required: bool,
+ is_instance_method: bool)
+ where Args: EncodeArguments,
+ Ret: Encode {
+ let encs = Args::encodings();
+ let encs = encs.as_ref();
+ let sel_args = count_args(sel);
+ assert!(sel_args == encs.len(),
+ "Selector accepts {} arguments, but function accepts {}",
+ sel_args, encs.len(),
+ );
+ let types = method_type_encoding(&Ret::encode(), encs);
+ unsafe {
+ runtime::protocol_addMethodDescription(
+ self.proto, sel, types.as_ptr(), is_required as BOOL, is_instance_method as BOOL);
+ }
+ }
+
+ /// Adds an instance method declaration with a given description to self.
+ pub fn add_method_description<Args, Ret>(&mut self, sel: Sel, is_required: bool)
+ where Args: EncodeArguments,
+ Ret: Encode {
+ self.add_method_description_common::<Args, Ret>(sel, is_required, true)
+ }
+
+ /// Adds a class method declaration with a given description to self.
+ pub fn add_class_method_description<Args, Ret>(&mut self, sel: Sel, is_required: bool)
+ where Args: EncodeArguments,
+ Ret: Encode {
+ self.add_method_description_common::<Args, Ret>(sel, is_required, false)
+ }
+
+ /// Adds a requirement on another protocol.
+ pub fn add_protocol(&mut self, proto: &Protocol) {
+ unsafe {
+ runtime::protocol_addProtocol(self.proto, proto);
+ }
+ }
+
+ /// Registers self, consuming it and returning a reference to the
+ /// newly registered `Protocol`.
+ pub fn register(self) -> &'static Protocol {
+ unsafe {
+ runtime::objc_registerProtocol(self.proto);
+ &*self.proto
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use test_utils;
+
+ #[test]
+ fn test_custom_class() {
+ // Registering the custom class is in test_utils
+ let obj = test_utils::custom_object();
+ unsafe {
+ let _: () = msg_send![obj, setFoo:13u32];
+ let result: u32 = msg_send![obj, foo];
+ assert!(result == 13);
+ }
+ }
+
+ #[test]
+ fn test_class_method() {
+ let cls = test_utils::custom_class();
+ unsafe {
+ let result: u32 = msg_send![cls, classFoo];
+ assert!(result == 7);
+ }
+ }
+}
diff --git a/third_party/rust/objc/src/encode.rs b/third_party/rust/objc/src/encode.rs
new file mode 100644
index 0000000000..921479da98
--- /dev/null
+++ b/third_party/rust/objc/src/encode.rs
@@ -0,0 +1,279 @@
+use std::ffi::CStr;
+use std::fmt;
+use std::os::raw::{c_char, c_void};
+use std::str;
+use malloc_buf::MallocBuffer;
+
+use runtime::{Class, Object, Sel};
+
+const QUALIFIERS: &'static [char] = &[
+ 'r', // const
+ 'n', // in
+ 'N', // inout
+ 'o', // out
+ 'O', // bycopy
+ 'R', // byref
+ 'V', // oneway
+];
+
+#[cfg(target_pointer_width = "64")]
+const CODE_INLINE_CAP: usize = 30;
+
+#[cfg(target_pointer_width = "32")]
+const CODE_INLINE_CAP: usize = 14;
+
+enum Code {
+ Slice(&'static str),
+ Owned(String),
+ Inline(u8, [u8; CODE_INLINE_CAP]),
+ Malloc(MallocBuffer<u8>)
+}
+
+/// An Objective-C type encoding.
+///
+/// For more information, see Apple's documentation:
+/// <https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html>
+pub struct Encoding {
+ code: Code,
+}
+
+impl Encoding {
+ /// Constructs an `Encoding` from its string representation.
+ /// Unsafe because the caller must ensure the string is a valid encoding.
+ pub unsafe fn from_str(code: &str) -> Encoding {
+ from_str(code)
+ }
+
+ /// Returns self as a `str`.
+ pub fn as_str(&self) -> &str {
+ match self.code {
+ Code::Slice(code) => code,
+ Code::Owned(ref code) => code,
+ Code::Inline(len, ref bytes) => unsafe {
+ str::from_utf8_unchecked(&bytes[..len as usize])
+ },
+ Code::Malloc(ref buf) => unsafe {
+ str::from_utf8_unchecked(&buf[..buf.len() - 1])
+ },
+ }
+ }
+}
+
+impl Clone for Encoding {
+ fn clone(&self) -> Encoding {
+ if let Code::Slice(code) = self.code {
+ from_static_str(code)
+ } else {
+ from_str(self.as_str())
+ }
+ }
+}
+
+impl PartialEq for Encoding {
+ fn eq(&self, other: &Encoding) -> bool {
+ // strip qualifiers when comparing
+ let s = self.as_str().trim_left_matches(QUALIFIERS);
+ let o = other.as_str().trim_left_matches(QUALIFIERS);
+ s == o
+ }
+}
+
+impl fmt::Debug for Encoding {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.as_str())
+ }
+}
+
+pub fn from_static_str(code: &'static str) -> Encoding {
+ Encoding { code: Code::Slice(code) }
+}
+
+pub fn from_str(code: &str) -> Encoding {
+ if code.len() > CODE_INLINE_CAP {
+ Encoding { code: Code::Owned(code.to_owned()) }
+ } else {
+ let mut bytes = [0; CODE_INLINE_CAP];
+ for (dst, byte) in bytes.iter_mut().zip(code.bytes()) {
+ *dst = byte;
+ }
+ Encoding { code: Code::Inline(code.len() as u8, bytes) }
+ }
+}
+
+pub unsafe fn from_malloc_str(ptr: *mut c_char) -> Encoding {
+ let s = CStr::from_ptr(ptr);
+ let bytes = s.to_bytes_with_nul();
+ assert!(str::from_utf8(bytes).is_ok());
+ let buf = MallocBuffer::new(ptr as *mut u8, bytes.len()).unwrap();
+ Encoding { code: Code::Malloc(buf) }
+}
+
+/// Types that have an Objective-C type encoding.
+///
+/// Unsafe because Objective-C will make assumptions about the type (like its
+/// size and alignment) from its encoding, so the implementer must verify that
+/// the encoding is accurate.
+pub unsafe trait Encode {
+ /// Returns the Objective-C type encoding for Self.
+ fn encode() -> Encoding;
+}
+
+macro_rules! encode_impls {
+ ($($t:ty : $s:expr,)*) => ($(
+ unsafe impl Encode for $t {
+ fn encode() -> Encoding { from_static_str($s) }
+ }
+ )*);
+}
+
+encode_impls!(
+ i8: "c",
+ i16: "s",
+ i32: "i",
+ i64: "q",
+ u8: "C",
+ u16: "S",
+ u32: "I",
+ u64: "Q",
+ f32: "f",
+ f64: "d",
+ bool: "B",
+ (): "v",
+ *mut c_char: "*",
+ *const c_char: "r*",
+ *mut c_void: "^v",
+ *const c_void: "r^v",
+ Sel: ":",
+);
+
+unsafe impl Encode for isize {
+ #[cfg(target_pointer_width = "32")]
+ fn encode() -> Encoding { i32::encode() }
+
+ #[cfg(target_pointer_width = "64")]
+ fn encode() -> Encoding { i64::encode() }
+}
+
+unsafe impl Encode for usize {
+ #[cfg(target_pointer_width = "32")]
+ fn encode() -> Encoding { u32::encode() }
+
+ #[cfg(target_pointer_width = "64")]
+ fn encode() -> Encoding { u64::encode() }
+}
+
+macro_rules! encode_message_impl {
+ ($code:expr, $name:ident) => (
+ encode_message_impl!($code, $name,);
+ );
+ ($code:expr, $name:ident, $($t:ident),*) => (
+ unsafe impl<'a $(, $t)*> $crate::Encode for &'a $name<$($t),*> {
+ fn encode() -> Encoding { from_static_str($code) }
+ }
+
+ unsafe impl<'a $(, $t)*> $crate::Encode for &'a mut $name<$($t),*> {
+ fn encode() -> Encoding { from_static_str($code) }
+ }
+
+ unsafe impl<'a $(, $t)*> $crate::Encode for Option<&'a $name<$($t),*>> {
+ fn encode() -> Encoding { from_static_str($code) }
+ }
+
+ unsafe impl<'a $(, $t)*> $crate::Encode for Option<&'a mut $name<$($t),*>> {
+ fn encode() -> Encoding { from_static_str($code) }
+ }
+
+ unsafe impl<$($t),*> $crate::Encode for *const $name<$($t),*> {
+ fn encode() -> Encoding { from_static_str($code) }
+ }
+
+ unsafe impl<$($t),*> $crate::Encode for *mut $name<$($t),*> {
+ fn encode() -> Encoding { from_static_str($code) }
+ }
+ );
+}
+
+encode_message_impl!("@", Object);
+
+encode_message_impl!("#", Class);
+
+/// Types that represent a group of arguments, where each has an Objective-C
+/// type encoding.
+pub trait EncodeArguments {
+ /// The type as which the encodings for Self will be returned.
+ type Encs: AsRef<[Encoding]>;
+
+ /// Returns the Objective-C type encodings for Self.
+ fn encodings() -> Self::Encs;
+}
+
+macro_rules! count_idents {
+ () => (0);
+ ($a:ident) => (1);
+ ($a:ident, $($b:ident),+) => (1 + count_idents!($($b),*));
+}
+
+macro_rules! encode_args_impl {
+ ($($t:ident),*) => (
+ impl<$($t: Encode),*> EncodeArguments for ($($t,)*) {
+ type Encs = [Encoding; count_idents!($($t),*)];
+
+ fn encodings() -> Self::Encs {
+ [
+ $($t::encode()),*
+ ]
+ }
+ }
+ );
+}
+
+encode_args_impl!();
+encode_args_impl!(A);
+encode_args_impl!(A, B);
+encode_args_impl!(A, B, C);
+encode_args_impl!(A, B, C, D);
+encode_args_impl!(A, B, C, D, E);
+encode_args_impl!(A, B, C, D, E, F);
+encode_args_impl!(A, B, C, D, E, F, G);
+encode_args_impl!(A, B, C, D, E, F, G, H);
+encode_args_impl!(A, B, C, D, E, F, G, H, I);
+encode_args_impl!(A, B, C, D, E, F, G, H, I, J);
+encode_args_impl!(A, B, C, D, E, F, G, H, I, J, K);
+encode_args_impl!(A, B, C, D, E, F, G, H, I, J, K, L);
+
+#[cfg(test)]
+mod tests {
+ use runtime::{Class, Object, Sel};
+ use super::{Encode, Encoding};
+
+ #[test]
+ fn test_encode() {
+ assert!(u32::encode().as_str() == "I");
+ assert!(<()>::encode().as_str() == "v");
+ assert!(<&Object>::encode().as_str() == "@");
+ assert!(<*mut Object>::encode().as_str() == "@");
+ assert!(<&Class>::encode().as_str() == "#");
+ assert!(Sel::encode().as_str() == ":");
+ }
+
+ #[test]
+ fn test_inline_encoding() {
+ let enc = unsafe { Encoding::from_str("C") };
+ assert!(enc.as_str() == "C");
+
+ let enc2 = enc.clone();
+ assert!(enc2 == enc);
+ assert!(enc2.as_str() == "C");
+ }
+
+ #[test]
+ fn test_owned_encoding() {
+ let s = "{Test=CCCCCCCCCCCCCCCCCCCCCCCCC}";
+ let enc = unsafe { Encoding::from_str(s) };
+ assert!(enc.as_str() == s);
+
+ let enc2 = enc.clone();
+ assert!(enc2 == enc);
+ assert!(enc2.as_str() == s);
+ }
+}
diff --git a/third_party/rust/objc/src/exception.rs b/third_party/rust/objc/src/exception.rs
new file mode 100644
index 0000000000..6fcfa7e87a
--- /dev/null
+++ b/third_party/rust/objc/src/exception.rs
@@ -0,0 +1,11 @@
+use objc_exception;
+
+use rc::StrongPtr;
+use runtime::Object;
+
+pub unsafe fn try<F, R>(closure: F) -> Result<R, StrongPtr>
+ where F: FnOnce() -> R {
+ objc_exception::try(closure).map_err(|exception| {
+ StrongPtr::new(exception as *mut Object)
+ })
+}
diff --git a/third_party/rust/objc/src/lib.rs b/third_party/rust/objc/src/lib.rs
new file mode 100644
index 0000000000..cdd30f5fd5
--- /dev/null
+++ b/third_party/rust/objc/src/lib.rs
@@ -0,0 +1,90 @@
+/*!
+Objective-C Runtime bindings and wrapper for Rust.
+
+# Messaging objects
+
+Objective-C objects can be messaged using the [`msg_send!`](macro.msg_send!.html) macro:
+
+``` no_run
+# #[macro_use] extern crate objc;
+# use objc::runtime::{BOOL, Class, Object};
+# fn main() {
+# unsafe {
+let cls = class!(NSObject);
+let obj: *mut Object = msg_send![cls, new];
+let hash: usize = msg_send![obj, hash];
+let is_kind: BOOL = msg_send![obj, isKindOfClass:cls];
+// Even void methods must have their return type annotated
+let _: () = msg_send![obj, release];
+# }
+# }
+```
+
+# Reference counting
+
+Utilities for reference counting Objective-C objects are provided in the
+[`rc`](rc/index.html) module.
+
+# Declaring classes
+
+Objective-C classes can even be declared from Rust using the functionality of
+the [`declare`](declare/index.html) module.
+
+# Exceptions
+
+By default, if the `msg_send!` macro causes an exception to be thrown, this
+will unwind into Rust resulting in unsafe, undefined behavior.
+However, this crate has an `"exception"` feature which, when enabled, wraps
+each `msg_send!` in a `@try`/`@catch` and panics if an exception is caught,
+preventing Objective-C from unwinding into Rust.
+
+# Message type verification
+
+The Objective-C runtime includes encodings for each method that describe the
+argument and return types. This crate can take advantage of these encodings to
+verify that the types used in Rust match the types encoded for the method.
+
+To use this functionality, enable the `"verify_message"` feature.
+With this feature enabled, type checking is performed for every message send,
+which also requires that all arguments and return values for all messages
+implement `Encode`.
+
+If this requirement is burdensome or you'd rather
+just verify specific messages, you can call the
+[`Message::verify_message`](trait.Message.html#method.verify_message) method
+for specific selectors.
+
+# Support for other Operating Systems
+
+The bindings can be used on Linux or *BSD utilizing the
+[GNUstep Objective-C runtime](https://www.github.com/gnustep/libobjc2).
+*/
+
+#![crate_name = "objc"]
+#![crate_type = "lib"]
+
+#![warn(missing_docs)]
+
+extern crate malloc_buf;
+#[cfg(feature = "exception")]
+extern crate objc_exception;
+
+pub use encode::{Encode, EncodeArguments, Encoding};
+pub use message::{Message, MessageArguments, MessageError};
+
+pub use message::send_message as __send_message;
+pub use message::send_super_message as __send_super_message;
+
+#[macro_use]
+mod macros;
+
+pub mod runtime;
+pub mod declare;
+pub mod rc;
+mod encode;
+#[cfg(feature = "exception")]
+mod exception;
+mod message;
+
+#[cfg(test)]
+mod test_utils;
diff --git a/third_party/rust/objc/src/macros.rs b/third_party/rust/objc/src/macros.rs
new file mode 100644
index 0000000000..c791f20ff9
--- /dev/null
+++ b/third_party/rust/objc/src/macros.rs
@@ -0,0 +1,148 @@
+/**
+Gets a reference to a `Class`.
+
+Panics if no class with the given name can be found.
+To check for a class that may not exist, use `Class::get`.
+
+# Example
+``` no_run
+# #[macro_use] extern crate objc;
+# fn main() {
+let cls = class!(NSObject);
+# }
+```
+*/
+#[macro_export]
+macro_rules! class {
+ ($name:ident) => ({
+ #[allow(deprecated)]
+ #[inline(always)]
+ fn get_class(name: &str) -> Option<&'static $crate::runtime::Class> {
+ unsafe {
+ #[cfg_attr(feature = "cargo-clippy", allow(replace_consts))]
+ static CLASS: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::ATOMIC_USIZE_INIT;
+ // `Relaxed` should be fine since `objc_getClass` is thread-safe.
+ let ptr = CLASS.load(::std::sync::atomic::Ordering::Relaxed) as *const $crate::runtime::Class;
+ if ptr.is_null() {
+ let cls = $crate::runtime::objc_getClass(name.as_ptr() as *const _);
+ CLASS.store(cls as usize, ::std::sync::atomic::Ordering::Relaxed);
+ if cls.is_null() { None } else { Some(&*cls) }
+ } else {
+ Some(&*ptr)
+ }
+ }
+ }
+ match get_class(concat!(stringify!($name), '\0')) {
+ Some(cls) => cls,
+ None => panic!("Class with name {} could not be found", stringify!($name)),
+ }
+ })
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! sel_impl {
+ // Declare a function to hide unsafety, otherwise we can trigger the
+ // unused_unsafe lint; see rust-lang/rust#8472
+ ($name:expr) => ({
+ #[allow(deprecated)]
+ #[inline(always)]
+ fn register_sel(name: &str) -> $crate::runtime::Sel {
+ unsafe {
+ #[cfg_attr(feature = "cargo-clippy", allow(replace_consts))]
+ static SEL: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::ATOMIC_USIZE_INIT;
+ let ptr = SEL.load(::std::sync::atomic::Ordering::Relaxed) as *const ::std::os::raw::c_void;
+ // It should be fine to use `Relaxed` ordering here because `sel_registerName` is
+ // thread-safe.
+ if ptr.is_null() {
+ let sel = $crate::runtime::sel_registerName(name.as_ptr() as *const _);
+ SEL.store(sel.as_ptr() as usize, ::std::sync::atomic::Ordering::Relaxed);
+ sel
+ } else {
+ $crate::runtime::Sel::from_ptr(ptr)
+ }
+ }
+ }
+ register_sel($name)
+ })
+}
+
+/**
+Registers a selector, returning a `Sel`.
+
+# Example
+```
+# #[macro_use] extern crate objc;
+# fn main() {
+let sel = sel!(description);
+let sel = sel!(setObject:forKey:);
+# }
+```
+*/
+#[macro_export]
+macro_rules! sel {
+ ($name:ident) => ({sel_impl!(concat!(stringify!($name), '\0'))});
+ ($($name:ident :)+) => ({sel_impl!(concat!($(stringify!($name), ':'),+, '\0'))});
+}
+
+/**
+Sends a message to an object.
+
+The first argument can be any type that dereferences to a type that implements
+`Message`, like a reference, pointer, or an `Id`.
+The syntax is similar to the message syntax in Objective-C.
+Variadic arguments are not currently supported.
+
+# Example
+``` no_run
+# #[macro_use] extern crate objc;
+# use objc::runtime::Object;
+# fn main() {
+# unsafe {
+let obj: *mut Object;
+# let obj: *mut Object = 0 as *mut Object;
+let description: *const Object = msg_send![obj, description];
+let _: () = msg_send![obj, setArg1:1 arg2:2];
+# }
+# }
+```
+*/
+#[macro_export]
+macro_rules! msg_send {
+ (super($obj:expr, $superclass:expr), $name:ident) => ({
+ let sel = sel!($name);
+ let result;
+ match $crate::__send_super_message(&*$obj, $superclass, sel, ()) {
+ Err(s) => panic!("{}", s),
+ Ok(r) => result = r,
+ }
+ result
+ });
+ (super($obj:expr, $superclass:expr), $($name:ident : $arg:expr)+) => ({
+ let sel = sel!($($name:)+);
+ let result;
+ match $crate::__send_super_message(&*$obj, $superclass, sel, ($($arg,)*)) {
+ Err(s) => panic!("{}", s),
+ Ok(r) => result = r,
+ }
+ result
+ });
+ ($obj:expr, $name:ident) => ({
+ let sel = sel!($name);
+ let result;
+ match $crate::__send_message(&*$obj, sel, ()) {
+ Err(s) => panic!("{}", s),
+ Ok(r) => result = r,
+ }
+ result
+ });
+ ($obj:expr, $($name:ident : $arg:expr)+) => ({
+ let sel = sel!($($name:)+);
+ let result;
+ match $crate::__send_message(&*$obj, sel, ($($arg,)*)) {
+ Err(s) => panic!("{}", s),
+ Ok(r) => result = r,
+ }
+ result
+ });
+}
diff --git a/third_party/rust/objc/src/message/apple/arm.rs b/third_party/rust/objc/src/message/apple/arm.rs
new file mode 100644
index 0000000000..d26defc0de
--- /dev/null
+++ b/third_party/rust/objc/src/message/apple/arm.rs
@@ -0,0 +1,40 @@
+use std::any::{Any, TypeId};
+use std::mem;
+
+use runtime::Imp;
+
+extern {
+ fn objc_msgSend();
+ fn objc_msgSend_stret();
+
+ fn objc_msgSendSuper();
+ fn objc_msgSendSuper_stret();
+}
+
+pub fn msg_send_fn<R: Any>() -> Imp {
+ // Double-word sized fundamental data types don't use stret,
+ // but any composite type larger than 4 bytes does.
+ // <http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042e/IHI0042E_aapcs.pdf>
+
+ let type_id = TypeId::of::<R>();
+ if mem::size_of::<R>() <= 4 ||
+ type_id == TypeId::of::<i64>() ||
+ type_id == TypeId::of::<u64>() ||
+ type_id == TypeId::of::<f64>() {
+ objc_msgSend
+ } else {
+ objc_msgSend_stret
+ }
+}
+
+pub fn msg_send_super_fn<R: Any>() -> Imp {
+ let type_id = TypeId::of::<R>();
+ if mem::size_of::<R>() <= 4 ||
+ type_id == TypeId::of::<i64>() ||
+ type_id == TypeId::of::<u64>() ||
+ type_id == TypeId::of::<f64>() {
+ objc_msgSendSuper
+ } else {
+ objc_msgSendSuper_stret
+ }
+}
diff --git a/third_party/rust/objc/src/message/apple/arm64.rs b/third_party/rust/objc/src/message/apple/arm64.rs
new file mode 100644
index 0000000000..54cfc897c5
--- /dev/null
+++ b/third_party/rust/objc/src/message/apple/arm64.rs
@@ -0,0 +1,18 @@
+use runtime::Imp;
+
+extern {
+ fn objc_msgSend();
+
+ fn objc_msgSendSuper();
+}
+
+pub fn msg_send_fn<R>() -> Imp {
+ // stret is not even available in arm64.
+ // <https://twitter.com/gparker/status/378079715824660480>
+
+ objc_msgSend
+}
+
+pub fn msg_send_super_fn<R>() -> Imp {
+ objc_msgSendSuper
+}
diff --git a/third_party/rust/objc/src/message/apple/mod.rs b/third_party/rust/objc/src/message/apple/mod.rs
new file mode 100644
index 0000000000..30f59ca870
--- /dev/null
+++ b/third_party/rust/objc/src/message/apple/mod.rs
@@ -0,0 +1,40 @@
+use std::any::Any;
+
+use runtime::{Class, Object, Sel};
+use super::{Message, MessageArguments, MessageError, Super};
+
+#[cfg(target_arch = "x86")]
+#[path = "x86.rs"]
+mod arch;
+#[cfg(target_arch = "x86_64")]
+#[path = "x86_64.rs"]
+mod arch;
+#[cfg(target_arch = "arm")]
+#[path = "arm.rs"]
+mod arch;
+#[cfg(target_arch = "aarch64")]
+#[path = "arm64.rs"]
+mod arch;
+
+use self::arch::{msg_send_fn, msg_send_super_fn};
+
+pub unsafe fn send_unverified<T, A, R>(obj: *const T, sel: Sel, args: A)
+ -> Result<R, MessageError>
+ where T: Message, A: MessageArguments, R: Any {
+ let receiver = obj as *mut T as *mut Object;
+ let msg_send_fn = msg_send_fn::<R>();
+ objc_try!({
+ A::invoke(msg_send_fn, receiver, sel, args)
+ })
+}
+
+pub unsafe fn send_super_unverified<T, A, R>(obj: *const T, superclass: &Class,
+ sel: Sel, args: A) -> Result<R, MessageError>
+ where T: Message, A: MessageArguments, R: Any {
+ let sup = Super { receiver: obj as *mut T as *mut Object, superclass: superclass };
+ let receiver = &sup as *const Super as *mut Object;
+ let msg_send_fn = msg_send_super_fn::<R>();
+ objc_try!({
+ A::invoke(msg_send_fn, receiver, sel, args)
+ })
+}
diff --git a/third_party/rust/objc/src/message/apple/x86.rs b/third_party/rust/objc/src/message/apple/x86.rs
new file mode 100644
index 0000000000..93422de24b
--- /dev/null
+++ b/third_party/rust/objc/src/message/apple/x86.rs
@@ -0,0 +1,40 @@
+use std::any::{Any, TypeId};
+use std::mem;
+
+use runtime::Imp;
+
+extern {
+ fn objc_msgSend();
+ fn objc_msgSend_fpret();
+ fn objc_msgSend_stret();
+
+ fn objc_msgSendSuper();
+ fn objc_msgSendSuper_stret();
+}
+
+pub fn msg_send_fn<R: Any>() -> Imp {
+ // Structures 1 or 2 bytes in size are placed in EAX.
+ // Structures 4 or 8 bytes in size are placed in: EAX and EDX.
+ // Structures of other sizes are placed at the address supplied by the caller.
+ // <https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/130-IA-32_Function_Calling_Conventions/IA32.html>
+
+ let type_id = TypeId::of::<R>();
+ let size = mem::size_of::<R>();
+ if type_id == TypeId::of::<f32>() ||
+ type_id == TypeId::of::<f64>() {
+ objc_msgSend_fpret
+ } else if size == 0 || size == 1 || size == 2 || size == 4 || size == 8 {
+ objc_msgSend
+ } else {
+ objc_msgSend_stret
+ }
+}
+
+pub fn msg_send_super_fn<R: Any>() -> Imp {
+ let size = mem::size_of::<R>();
+ if size == 0 || size == 1 || size == 2 || size == 4 || size == 8 {
+ objc_msgSendSuper
+ } else {
+ objc_msgSendSuper_stret
+ }
+}
diff --git a/third_party/rust/objc/src/message/apple/x86_64.rs b/third_party/rust/objc/src/message/apple/x86_64.rs
new file mode 100644
index 0000000000..49c90ce53e
--- /dev/null
+++ b/third_party/rust/objc/src/message/apple/x86_64.rs
@@ -0,0 +1,32 @@
+use std::mem;
+
+use runtime::Imp;
+
+extern {
+ fn objc_msgSend();
+ fn objc_msgSend_stret();
+
+ fn objc_msgSendSuper();
+ fn objc_msgSendSuper_stret();
+}
+
+pub fn msg_send_fn<R>() -> Imp {
+ // If the size of an object is larger than two eightbytes, it has class MEMORY.
+ // If the type has class MEMORY, then the caller provides space for the return
+ // value and passes the address of this storage.
+ // <http://people.freebsd.org/~obrien/amd64-elf-abi.pdf>
+
+ if mem::size_of::<R>() <= 16 {
+ objc_msgSend
+ } else {
+ objc_msgSend_stret
+ }
+}
+
+pub fn msg_send_super_fn<R>() -> Imp {
+ if mem::size_of::<R>() <= 16 {
+ objc_msgSendSuper
+ } else {
+ objc_msgSendSuper_stret
+ }
+}
diff --git a/third_party/rust/objc/src/message/gnustep.rs b/third_party/rust/objc/src/message/gnustep.rs
new file mode 100644
index 0000000000..2e28689cef
--- /dev/null
+++ b/third_party/rust/objc/src/message/gnustep.rs
@@ -0,0 +1,35 @@
+use std::any::Any;
+use std::mem;
+
+use runtime::{Class, Object, Imp, Sel};
+use super::{Message, MessageArguments, MessageError, Super};
+
+extern {
+ fn objc_msg_lookup(receiver: *mut Object, op: Sel) -> Imp;
+ fn objc_msg_lookup_super(sup: *const Super, sel: Sel) -> Imp;
+}
+
+pub unsafe fn send_unverified<T, A, R>(obj: *const T, sel: Sel, args: A)
+ -> Result<R, MessageError>
+ where T: Message, A: MessageArguments, R: Any {
+ if obj.is_null() {
+ return mem::zeroed();
+ }
+
+ let receiver = obj as *mut T as *mut Object;
+ let msg_send_fn = objc_msg_lookup(receiver, sel);
+ objc_try!({
+ A::invoke(msg_send_fn, receiver, sel, args)
+ })
+}
+
+pub unsafe fn send_super_unverified<T, A, R>(obj: *const T, superclass: &Class,
+ sel: Sel, args: A) -> Result<R, MessageError>
+ where T: Message, A: MessageArguments, R: Any {
+ let receiver = obj as *mut T as *mut Object;
+ let sup = Super { receiver: receiver, superclass: superclass };
+ let msg_send_fn = objc_msg_lookup_super(&sup, sel);
+ objc_try!({
+ A::invoke(msg_send_fn, receiver, sel, args)
+ })
+}
diff --git a/third_party/rust/objc/src/message/mod.rs b/third_party/rust/objc/src/message/mod.rs
new file mode 100644
index 0000000000..cb11e993a8
--- /dev/null
+++ b/third_party/rust/objc/src/message/mod.rs
@@ -0,0 +1,296 @@
+use std::any::Any;
+use std::error::Error;
+use std::fmt;
+use std::mem;
+
+use runtime::{Class, Imp, Object, Sel};
+use {Encode, EncodeArguments};
+
+#[cfg(feature = "exception")]
+macro_rules! objc_try {
+ ($b:block) => (
+ $crate::exception::try(|| $b).map_err(|exception|
+ if exception.is_null() {
+ MessageError("Uncaught exception nil".to_owned())
+ } else {
+ MessageError(format!("Uncaught exception {:?}", &**exception))
+ }
+ )
+ )
+}
+
+#[cfg(not(feature = "exception"))]
+macro_rules! objc_try {
+ ($b:block) => (Ok($b))
+}
+
+mod verify;
+
+#[cfg(any(target_os = "macos", target_os = "ios"))]
+#[path = "apple/mod.rs"]
+mod platform;
+#[cfg(not(any(target_os = "macos", target_os = "ios")))]
+#[path = "gnustep.rs"]
+mod platform;
+
+use self::platform::{send_unverified, send_super_unverified};
+use self::verify::verify_message_signature;
+
+/// Specifies the superclass of an instance.
+#[repr(C)]
+pub struct Super {
+ /// Specifies an instance of a class.
+ pub receiver: *mut Object,
+ /// Specifies the particular superclass of the instance to message.
+ pub superclass: *const Class,
+}
+
+/// Types that may be sent Objective-C messages.
+/// For example: objects, classes, and blocks.
+pub unsafe trait Message {
+ /**
+ Sends a message to self with the given selector and arguments.
+
+ The correct version of `objc_msgSend` will be chosen based on the
+ return type. For more information, see Apple's documentation:
+ <https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ObjCRuntimeRef/index.html#//apple_ref/doc/uid/TP40001418-CH1g-88778>
+
+ If the selector is known at compile-time, it is recommended to use the
+ `msg_send!` macro rather than this method.
+ */
+ #[cfg(not(feature = "verify_message"))]
+ unsafe fn send_message<A, R>(&self, sel: Sel, args: A)
+ -> Result<R, MessageError>
+ where Self: Sized, A: MessageArguments, R: Any {
+ send_message(self, sel, args)
+ }
+
+ #[cfg(feature = "verify_message")]
+ unsafe fn send_message<A, R>(&self, sel: Sel, args: A)
+ -> Result<R, MessageError>
+ where Self: Sized, A: MessageArguments + EncodeArguments,
+ R: Any + Encode {
+ send_message(self, sel, args)
+ }
+
+ /**
+ Verifies that the argument and return types match the encoding of the
+ method for the given selector.
+
+ This will look up the encoding of the method for the given selector, `sel`,
+ and return a `MessageError` if any encodings differ for the arguments `A`
+ and return type `R`.
+
+ # Example
+ ``` no_run
+ # #[macro_use] extern crate objc;
+ # use objc::runtime::{BOOL, Class, Object};
+ # use objc::Message;
+ # fn main() {
+ let obj: &Object;
+ # obj = unsafe { msg_send![class!(NSObject), new] };
+ let sel = sel!(isKindOfClass:);
+ // Verify isKindOfClass: takes one Class and returns a BOOL
+ let result = obj.verify_message::<(&Class,), BOOL>(sel);
+ assert!(result.is_ok());
+ # }
+ ```
+ */
+ fn verify_message<A, R>(&self, sel: Sel) -> Result<(), MessageError>
+ where Self: Sized, A: EncodeArguments, R: Encode {
+ let obj = unsafe { &*(self as *const _ as *const Object) };
+ verify_message_signature::<A, R>(obj.class(), sel)
+ }
+}
+
+unsafe impl Message for Object { }
+
+unsafe impl Message for Class { }
+
+/// Types that may be used as the arguments of an Objective-C message.
+pub trait MessageArguments: Sized {
+ /// Invoke an `Imp` with the given object, selector, and arguments.
+ ///
+ /// This method is the primitive used when sending messages and should not
+ /// be called directly; instead, use the `msg_send!` macro or, in cases
+ /// with a dynamic selector, the `Message::send_message` method.
+ unsafe fn invoke<R>(imp: Imp, obj: *mut Object, sel: Sel, args: Self) -> R
+ where R: Any;
+}
+
+macro_rules! message_args_impl {
+ ($($a:ident : $t:ident),*) => (
+ impl<$($t),*> MessageArguments for ($($t,)*) {
+ unsafe fn invoke<R>(imp: Imp, obj: *mut Object, sel: Sel, ($($a,)*): Self) -> R
+ where R: Any {
+ let imp: unsafe extern fn(*mut Object, Sel $(, $t)*) -> R =
+ mem::transmute(imp);
+ imp(obj, sel $(, $a)*)
+ }
+ }
+ );
+}
+
+message_args_impl!();
+message_args_impl!(a: A);
+message_args_impl!(a: A, b: B);
+message_args_impl!(a: A, b: B, c: C);
+message_args_impl!(a: A, b: B, c: C, d: D);
+message_args_impl!(a: A, b: B, c: C, d: D, e: E);
+message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F);
+message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G);
+message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
+message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
+message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
+message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K);
+message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L);
+
+/**
+An error encountered while attempting to send a message.
+
+Currently, an error may be returned in two cases:
+
+* an Objective-C exception is thrown and the `exception` feature is enabled
+* the encodings of the arguments do not match the encoding of the method
+ and the `verify_message` feature is enabled
+*/
+#[derive(Debug)]
+pub struct MessageError(String);
+
+impl fmt::Display for MessageError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(&self.0, f)
+ }
+}
+
+impl Error for MessageError {
+ fn description(&self) -> &str {
+ &self.0
+ }
+}
+
+#[doc(hidden)]
+#[inline(always)]
+#[cfg(not(feature = "verify_message"))]
+pub unsafe fn send_message<T, A, R>(obj: *const T, sel: Sel, args: A)
+ -> Result<R, MessageError>
+ where T: Message, A: MessageArguments, R: Any {
+ send_unverified(obj, sel, args)
+}
+
+#[doc(hidden)]
+#[inline(always)]
+#[cfg(feature = "verify_message")]
+pub unsafe fn send_message<T, A, R>(obj: *const T, sel: Sel, args: A)
+ -> Result<R, MessageError>
+ where T: Message, A: MessageArguments + EncodeArguments,
+ R: Any + Encode {
+ let cls = if obj.is_null() {
+ return Err(MessageError(format!("Messaging {:?} to nil", sel)));
+ } else {
+ (*(obj as *const Object)).class()
+ };
+
+ verify_message_signature::<A, R>(cls, sel).and_then(|_| {
+ send_unverified(obj, sel, args)
+ })
+}
+
+#[doc(hidden)]
+#[inline(always)]
+#[cfg(not(feature = "verify_message"))]
+pub unsafe fn send_super_message<T, A, R>(obj: *const T, superclass: &Class,
+ sel: Sel, args: A) -> Result<R, MessageError>
+ where T: Message, A: MessageArguments, R: Any {
+ send_super_unverified(obj, superclass, sel, args)
+}
+
+#[doc(hidden)]
+#[inline(always)]
+#[cfg(feature = "verify_message")]
+pub unsafe fn send_super_message<T, A, R>(obj: *const T, superclass: &Class,
+ sel: Sel, args: A) -> Result<R, MessageError>
+ where T: Message, A: MessageArguments + EncodeArguments,
+ R: Any + Encode {
+ if obj.is_null() {
+ return Err(MessageError(format!("Messaging {:?} to nil", sel)));
+ }
+
+ verify_message_signature::<A, R>(superclass, sel).and_then(|_| {
+ send_super_unverified(obj, superclass, sel, args)
+ })
+}
+
+#[cfg(test)]
+mod tests {
+ use test_utils;
+ use runtime::Object;
+ use super::Message;
+
+ #[test]
+ fn test_send_message() {
+ let obj = test_utils::custom_object();
+ let result: u32 = unsafe {
+ let _: () = msg_send![obj, setFoo:4u32];
+ msg_send![obj, foo]
+ };
+ assert!(result == 4);
+ }
+
+ #[test]
+ fn test_send_message_stret() {
+ let obj = test_utils::custom_object();
+ let result: test_utils::CustomStruct = unsafe {
+ msg_send![obj, customStruct]
+ };
+ let expected = test_utils::CustomStruct { a: 1, b:2, c: 3, d: 4 };
+ assert!(result == expected);
+ }
+
+ #[cfg(not(feature = "verify_message"))]
+ #[test]
+ fn test_send_message_nil() {
+ let nil: *mut Object = ::std::ptr::null_mut();
+ let result: usize = unsafe {
+ msg_send![nil, hash]
+ };
+ assert!(result == 0);
+
+ let result: *mut Object = unsafe {
+ msg_send![nil, description]
+ };
+ assert!(result.is_null());
+
+ let result: f64 = unsafe {
+ msg_send![nil, doubleValue]
+ };
+ assert!(result == 0.0);
+ }
+
+ #[test]
+ fn test_send_message_super() {
+ let obj = test_utils::custom_subclass_object();
+ let superclass = test_utils::custom_class();
+ unsafe {
+ let _: () = msg_send![obj, setFoo:4u32];
+ let foo: u32 = msg_send![super(obj, superclass), foo];
+ assert!(foo == 4);
+
+ // The subclass is overriden to return foo + 2
+ let foo: u32 = msg_send![obj, foo];
+ assert!(foo == 6);
+ }
+ }
+
+ #[test]
+ fn test_verify_message() {
+ let obj = test_utils::custom_object();
+ assert!(obj.verify_message::<(), u32>(sel!(foo)).is_ok());
+ assert!(obj.verify_message::<(u32,), ()>(sel!(setFoo:)).is_ok());
+
+ // Incorrect types
+ assert!(obj.verify_message::<(), u64>(sel!(setFoo:)).is_err());
+ // Unimplemented selector
+ assert!(obj.verify_message::<(u32,), ()>(sel!(setFoo)).is_err());
+ }
+}
diff --git a/third_party/rust/objc/src/message/verify.rs b/third_party/rust/objc/src/message/verify.rs
new file mode 100644
index 0000000000..61bd4ebb39
--- /dev/null
+++ b/third_party/rust/objc/src/message/verify.rs
@@ -0,0 +1,49 @@
+use runtime::{Class, Object, Sel};
+use {Encode, EncodeArguments};
+use super::MessageError;
+
+pub fn verify_message_signature<A, R>(cls: &Class, sel: Sel)
+ -> Result<(), MessageError>
+ where A: EncodeArguments, R: Encode {
+ let method = match cls.instance_method(sel) {
+ Some(method) => method,
+ None => return Err(MessageError(
+ format!("Method {:?} not found on class {:?}",
+ sel, cls)
+ )),
+ };
+
+ let ret = R::encode();
+ let expected_ret = method.return_type();
+ if ret != expected_ret {
+ return Err(MessageError(
+ format!("Return type code {:?} does not match expected {:?} for method {:?}",
+ ret, expected_ret, method.name())
+ ));
+ }
+
+ let self_and_cmd = [<*mut Object>::encode(), Sel::encode()];
+ let args = A::encodings();
+ let args = args.as_ref();
+
+ let count = self_and_cmd.len() + args.len();
+ let expected_count = method.arguments_count();
+ if count != expected_count {
+ return Err(MessageError(
+ format!("Method {:?} accepts {} arguments, but {} were given",
+ method.name(), expected_count, count)
+ ));
+ }
+
+ for (i, arg) in self_and_cmd.iter().chain(args).enumerate() {
+ let expected = method.argument_type(i).unwrap();
+ if *arg != expected {
+ return Err(MessageError(
+ format!("Method {:?} expected argument at index {} with type code {:?} but was given {:?}",
+ method.name(), i, expected, arg)
+ ));
+ }
+ }
+
+ Ok(())
+}
diff --git a/third_party/rust/objc/src/rc/autorelease.rs b/third_party/rust/objc/src/rc/autorelease.rs
new file mode 100644
index 0000000000..ad7a5e2847
--- /dev/null
+++ b/third_party/rust/objc/src/rc/autorelease.rs
@@ -0,0 +1,30 @@
+use std::os::raw::c_void;
+use runtime::{objc_autoreleasePoolPush, objc_autoreleasePoolPop};
+
+// we use a struct to ensure that objc_autoreleasePoolPop during unwinding.
+struct AutoReleaseHelper {
+ context: *mut c_void,
+}
+
+impl AutoReleaseHelper {
+ unsafe fn new() -> Self {
+ AutoReleaseHelper { context: objc_autoreleasePoolPush() }
+ }
+}
+
+impl Drop for AutoReleaseHelper {
+ fn drop(&mut self) {
+ unsafe { objc_autoreleasePoolPop(self.context) }
+ }
+}
+
+/**
+Execute `f` in the context of a new autorelease pool. The pool is drained
+after the execution of `f` completes.
+
+This corresponds to `@autoreleasepool` blocks in Objective-C and Swift.
+*/
+pub fn autoreleasepool<T, F: FnOnce() -> T>(f: F) -> T {
+ let _context = unsafe { AutoReleaseHelper::new() };
+ f()
+}
diff --git a/third_party/rust/objc/src/rc/mod.rs b/third_party/rust/objc/src/rc/mod.rs
new file mode 100644
index 0000000000..f6d26f7a1b
--- /dev/null
+++ b/third_party/rust/objc/src/rc/mod.rs
@@ -0,0 +1,123 @@
+/*!
+Utilities for reference counting Objective-C objects.
+
+The utilities of the `rc` module provide ARC-like semantics for working with
+Objective-C's reference counted objects in Rust.
+A `StrongPtr` retains an object and releases the object when dropped.
+A `WeakPtr` will not retain the object, but can be upgraded to a `StrongPtr`
+and safely fails if the object has been deallocated.
+
+These utilities are not intended to provide a fully safe interface, but can be
+useful when writing higher-level Rust wrappers for Objective-C code.
+
+For more information on Objective-C's reference counting, see Apple's documentation:
+<https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html>
+
+# Example
+
+``` no_run
+# #[macro_use] extern crate objc;
+# use objc::rc::{autoreleasepool, StrongPtr};
+# fn main() {
+// StrongPtr will release the object when dropped
+let obj = unsafe {
+ StrongPtr::new(msg_send![class!(NSObject), new])
+};
+
+// Cloning retains the object an additional time
+let cloned = obj.clone();
+autoreleasepool(|| {
+ // Autorelease consumes the StrongPtr, but won't
+ // actually release until the end of an autoreleasepool
+ cloned.autorelease();
+});
+
+// Weak references won't retain the object
+let weak = obj.weak();
+drop(obj);
+assert!(weak.load().is_null());
+# }
+```
+*/
+
+mod strong;
+mod weak;
+mod autorelease;
+
+pub use self::strong::StrongPtr;
+pub use self::weak::WeakPtr;
+pub use self::autorelease::autoreleasepool;
+
+// These tests use NSObject, which isn't present for GNUstep
+#[cfg(all(test, any(target_os = "macos", target_os = "ios")))]
+mod tests {
+ use runtime::Object;
+ use super::StrongPtr;
+ use super::autoreleasepool;
+
+ #[test]
+ fn test_strong_clone() {
+ fn retain_count(obj: *mut Object) -> usize {
+ unsafe { msg_send![obj, retainCount] }
+ }
+
+ let obj = unsafe {
+ StrongPtr::new(msg_send![class!(NSObject), new])
+ };
+ assert!(retain_count(*obj) == 1);
+
+ let cloned = obj.clone();
+ assert!(retain_count(*cloned) == 2);
+ assert!(retain_count(*obj) == 2);
+
+ drop(obj);
+ assert!(retain_count(*cloned) == 1);
+ }
+
+ #[test]
+ fn test_weak() {
+ let obj = unsafe {
+ StrongPtr::new(msg_send![class!(NSObject), new])
+ };
+ let weak = obj.weak();
+
+ let strong = weak.load();
+ assert!(*strong == *obj);
+ drop(strong);
+
+ drop(obj);
+ assert!(weak.load().is_null());
+ }
+
+ #[test]
+ fn test_weak_copy() {
+ let obj = unsafe {
+ StrongPtr::new(msg_send![class!(NSObject), new])
+ };
+ let weak = obj.weak();
+
+ let weak2 = weak.clone();
+ let strong = weak2.load();
+ assert!(*strong == *obj);
+ }
+
+ #[test]
+ fn test_autorelease() {
+ let obj = unsafe {
+ StrongPtr::new(msg_send![class!(NSObject), new])
+ };
+
+ fn retain_count(obj: *mut Object) -> usize {
+ unsafe { msg_send![obj, retainCount] }
+ }
+ let cloned = obj.clone();
+
+ autoreleasepool(|| {
+ obj.autorelease();
+ assert!(retain_count(*cloned) == 2);
+ });
+
+ // make sure that the autoreleased value has been released
+ assert!(retain_count(*cloned) == 1);
+ }
+}
diff --git a/third_party/rust/objc/src/rc/strong.rs b/third_party/rust/objc/src/rc/strong.rs
new file mode 100644
index 0000000000..a61881b6ff
--- /dev/null
+++ b/third_party/rust/objc/src/rc/strong.rs
@@ -0,0 +1,73 @@
+use std::fmt;
+use std::mem;
+use std::ops::Deref;
+
+use runtime::{Object, self};
+use super::WeakPtr;
+
+/// A pointer that strongly references an object, ensuring it won't be deallocated.
+pub struct StrongPtr(*mut Object);
+
+impl StrongPtr {
+ /// Constructs a `StrongPtr` to a newly created object that already has a
+ /// +1 retain count. This will not retain the object.
+ /// When dropped, the object will be released.
+ /// Unsafe because the caller must ensure the given object pointer is valid.
+ pub unsafe fn new(ptr: *mut Object) -> Self {
+ StrongPtr(ptr)
+ }
+
+ /// Retains the given object and constructs a `StrongPtr` to it.
+ /// When dropped, the object will be released.
+ /// Unsafe because the caller must ensure the given object pointer is valid.
+ pub unsafe fn retain(ptr: *mut Object) -> Self {
+ StrongPtr(runtime::objc_retain(ptr))
+ }
+
+ /// Autoreleases self, meaning that the object is not immediately released,
+ /// but will be when the autorelease pool is drained. A pointer to the
+ /// object is returned, but its validity is no longer ensured.
+ pub fn autorelease(self) -> *mut Object {
+ let ptr = self.0;
+ mem::forget(self);
+ unsafe {
+ runtime::objc_autorelease(ptr);
+ }
+ ptr
+ }
+
+ /// Returns a `WeakPtr` to self.
+ pub fn weak(&self) -> WeakPtr {
+ unsafe { WeakPtr::new(self.0) }
+ }
+}
+
+impl Drop for StrongPtr {
+ fn drop(&mut self) {
+ unsafe {
+ runtime::objc_release(self.0);
+ }
+ }
+}
+
+impl Clone for StrongPtr {
+ fn clone(&self) -> StrongPtr {
+ unsafe {
+ StrongPtr::retain(self.0)
+ }
+ }
+}
+
+impl Deref for StrongPtr {
+ type Target = *mut Object;
+
+ fn deref(&self) -> &*mut Object {
+ &self.0
+ }
+}
+
+impl fmt::Pointer for StrongPtr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Pointer::fmt(&self.0, f)
+ }
+}
diff --git a/third_party/rust/objc/src/rc/weak.rs b/third_party/rust/objc/src/rc/weak.rs
new file mode 100644
index 0000000000..289bd8dedb
--- /dev/null
+++ b/third_party/rust/objc/src/rc/weak.rs
@@ -0,0 +1,50 @@
+use std::cell::UnsafeCell;
+use std::ptr;
+
+use runtime::{Object, self};
+use super::StrongPtr;
+
+// Our pointer must have the same address even if we are moved, so Box it.
+// Although loading the WeakPtr may modify the pointer, it is thread safe,
+// so we must use an UnsafeCell to get a *mut without self being mutable.
+
+/// A pointer that weakly references an object, allowing to safely check
+/// whether it has been deallocated.
+pub struct WeakPtr(Box<UnsafeCell<*mut Object>>);
+
+impl WeakPtr {
+ /// Constructs a `WeakPtr` to the given object.
+ /// Unsafe because the caller must ensure the given object pointer is valid.
+ pub unsafe fn new(obj: *mut Object) -> Self {
+ let ptr = Box::new(UnsafeCell::new(ptr::null_mut()));
+ runtime::objc_initWeak(ptr.get(), obj);
+ WeakPtr(ptr)
+ }
+
+ /// Loads the object self points to, returning a `StrongPtr`.
+ /// If the object has been deallocated, the returned pointer will be null.
+ pub fn load(&self) -> StrongPtr {
+ unsafe {
+ let ptr = runtime::objc_loadWeakRetained(self.0.get());
+ StrongPtr::new(ptr)
+ }
+ }
+}
+
+impl Drop for WeakPtr {
+ fn drop(&mut self) {
+ unsafe {
+ runtime::objc_destroyWeak(self.0.get());
+ }
+ }
+}
+
+impl Clone for WeakPtr {
+ fn clone(&self) -> Self {
+ let ptr = Box::new(UnsafeCell::new(ptr::null_mut()));
+ unsafe {
+ runtime::objc_copyWeak(ptr.get(), self.0.get());
+ }
+ WeakPtr(ptr)
+ }
+}
diff --git a/third_party/rust/objc/src/runtime.rs b/third_party/rust/objc/src/runtime.rs
new file mode 100644
index 0000000000..3b533efe6f
--- /dev/null
+++ b/third_party/rust/objc/src/runtime.rs
@@ -0,0 +1,632 @@
+//! A Rust interface for the functionality of the Objective-C runtime.
+//!
+//! For more information on foreign functions, see Apple's documentation:
+//! <https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ObjCRuntimeRef/index.html>
+
+use std::ffi::{CStr, CString};
+use std::fmt;
+use std::os::raw::{c_char, c_int, c_uint, c_void};
+use std::ptr;
+use std::str;
+use malloc_buf::MallocBuffer;
+
+use encode;
+use {Encode, Encoding};
+
+/// The Objective-C `BOOL` type.
+///
+/// To convert an Objective-C `BOOL` into a Rust `bool`, compare it with `NO`.
+#[cfg(not(target_arch = "aarch64"))]
+pub type BOOL = ::std::os::raw::c_schar;
+/// The equivalent of true for Objective-C's `BOOL` type.
+#[cfg(not(target_arch = "aarch64"))]
+pub const YES: BOOL = 1;
+/// The equivalent of false for Objective-C's `BOOL` type.
+#[cfg(not(target_arch = "aarch64"))]
+pub const NO: BOOL = 0;
+
+#[cfg(target_arch = "aarch64")]
+pub type BOOL = bool;
+#[cfg(target_arch = "aarch64")]
+pub const YES: BOOL = true;
+#[cfg(target_arch = "aarch64")]
+pub const NO: BOOL = false;
+
+/// A type that represents a method selector.
+#[repr(C)]
+pub struct Sel {
+ ptr: *const c_void,
+}
+
+/// A marker type to be embedded into other types just so that they cannot be
+/// constructed externally.
+type PrivateMarker = [u8; 0];
+
+/// A type that represents an instance variable.
+#[repr(C)]
+pub struct Ivar {
+ _priv: PrivateMarker,
+}
+
+/// A type that represents a method in a class definition.
+#[repr(C)]
+pub struct Method {
+ _priv: PrivateMarker,
+}
+
+/// A type that represents an Objective-C class.
+#[repr(C)]
+pub struct Class {
+ _priv: PrivateMarker,
+}
+
+/// A type that represents an Objective-C protocol.
+#[repr(C)]
+pub struct Protocol {
+ _priv: PrivateMarker
+}
+
+/// A type that represents an instance of a class.
+#[repr(C)]
+pub struct Object {
+ _priv: PrivateMarker,
+}
+
+/// A pointer to the start of a method implementation.
+pub type Imp = unsafe extern fn();
+
+#[link(name = "objc", kind = "dylib")]
+extern {
+ pub fn sel_registerName(name: *const c_char) -> Sel;
+ pub fn sel_getName(sel: Sel) -> *const c_char;
+
+ pub fn class_getName(cls: *const Class) -> *const c_char;
+ pub fn class_getSuperclass(cls: *const Class) -> *const Class;
+ pub fn class_getInstanceSize(cls: *const Class) -> usize;
+ pub fn class_getInstanceMethod(cls: *const Class, sel: Sel) -> *const Method;
+ pub fn class_getInstanceVariable(cls: *const Class, name: *const c_char) -> *const Ivar;
+ pub fn class_copyMethodList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Method;
+ pub fn class_copyIvarList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Ivar;
+ pub fn class_addMethod(cls: *mut Class, name: Sel, imp: Imp, types: *const c_char) -> BOOL;
+ pub fn class_addIvar(cls: *mut Class, name: *const c_char, size: usize, alignment: u8, types: *const c_char) -> BOOL;
+ pub fn class_addProtocol(cls: *mut Class, proto: *const Protocol) -> BOOL;
+ pub fn class_conformsToProtocol(cls: *const Class, proto: *const Protocol) -> BOOL;
+ pub fn class_copyProtocolList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Protocol;
+
+ pub fn objc_allocateClassPair(superclass: *const Class, name: *const c_char, extraBytes: usize) -> *mut Class;
+ pub fn objc_disposeClassPair(cls: *mut Class);
+ pub fn objc_registerClassPair(cls: *mut Class);
+
+ pub fn class_createInstance(cls: *const Class, extraBytes: usize) -> *mut Object;
+ pub fn object_dispose(obj: *mut Object) -> *mut Object;
+ pub fn object_getClass(obj: *const Object) -> *const Class;
+
+ pub fn objc_getClassList(buffer: *mut *const Class, bufferLen: c_int) -> c_int;
+ pub fn objc_copyClassList(outCount: *mut c_uint) -> *mut *const Class;
+ pub fn objc_getClass(name: *const c_char) -> *const Class;
+ pub fn objc_getProtocol(name: *const c_char) -> *const Protocol;
+ pub fn objc_copyProtocolList(outCount: *mut c_uint) -> *mut *const Protocol;
+ pub fn objc_allocateProtocol(name: *const c_char) -> *mut Protocol;
+ pub fn objc_registerProtocol(proto: *mut Protocol);
+
+ pub fn objc_autoreleasePoolPush() -> *mut c_void;
+ pub fn objc_autoreleasePoolPop(context: *mut c_void);
+
+ pub fn protocol_addMethodDescription(proto: *mut Protocol, name: Sel, types: *const c_char, isRequiredMethod: BOOL,
+ isInstanceMethod: BOOL);
+ pub fn protocol_addProtocol(proto: *mut Protocol, addition: *const Protocol);
+ pub fn protocol_getName(proto: *const Protocol) -> *const c_char;
+ pub fn protocol_isEqual(proto: *const Protocol, other: *const Protocol) -> BOOL;
+ pub fn protocol_copyProtocolList(proto: *const Protocol, outCount: *mut c_uint) -> *mut *const Protocol;
+ pub fn protocol_conformsToProtocol(proto: *const Protocol, other: *const Protocol) -> BOOL;
+
+ pub fn ivar_getName(ivar: *const Ivar) -> *const c_char;
+ pub fn ivar_getOffset(ivar: *const Ivar) -> isize;
+ pub fn ivar_getTypeEncoding(ivar: *const Ivar) -> *const c_char;
+
+ pub fn method_getName(method: *const Method) -> Sel;
+ pub fn method_getImplementation(method: *const Method) -> Imp;
+ pub fn method_copyReturnType(method: *const Method) -> *mut c_char;
+ pub fn method_copyArgumentType(method: *const Method, index: c_uint) -> *mut c_char;
+ pub fn method_getNumberOfArguments(method: *const Method) -> c_uint;
+ pub fn method_setImplementation(method: *mut Method, imp: Imp) -> Imp;
+ pub fn method_exchangeImplementations(m1: *mut Method, m2: *mut Method);
+
+ pub fn objc_retain(obj: *mut Object) -> *mut Object;
+ pub fn objc_release(obj: *mut Object);
+ pub fn objc_autorelease(obj: *mut Object);
+
+ pub fn objc_loadWeakRetained(location: *mut *mut Object) -> *mut Object;
+ pub fn objc_initWeak(location: *mut *mut Object, obj: *mut Object) -> *mut Object;
+ pub fn objc_destroyWeak(location: *mut *mut Object);
+ pub fn objc_copyWeak(to: *mut *mut Object, from: *mut *mut Object);
+}
+
+impl Sel {
+ /// Registers a method with the Objective-C runtime system,
+ /// maps the method name to a selector, and returns the selector value.
+ pub fn register(name: &str) -> Sel {
+ let name = CString::new(name).unwrap();
+ unsafe {
+ sel_registerName(name.as_ptr())
+ }
+ }
+
+ /// Returns the name of the method specified by self.
+ pub fn name(&self) -> &str {
+ let name = unsafe {
+ CStr::from_ptr(sel_getName(*self))
+ };
+ str::from_utf8(name.to_bytes()).unwrap()
+ }
+
+ /// Wraps a raw pointer to a selector into a `Sel` object.
+ ///
+ /// This is almost never what you want; use `Sel::register()` instead.
+ #[inline]
+ pub unsafe fn from_ptr(ptr: *const c_void) -> Sel {
+ Sel {
+ ptr: ptr,
+ }
+ }
+
+ /// Returns a pointer to the raw selector.
+ #[inline]
+ pub fn as_ptr(&self) -> *const c_void {
+ self.ptr
+ }
+}
+
+impl PartialEq for Sel {
+ fn eq(&self, other: &Sel) -> bool {
+ self.ptr == other.ptr
+ }
+}
+
+impl Eq for Sel { }
+
+// Sel is safe to share across threads because it is immutable
+unsafe impl Sync for Sel { }
+unsafe impl Send for Sel { }
+
+impl Copy for Sel { }
+
+impl Clone for Sel {
+ fn clone(&self) -> Sel { *self }
+}
+
+impl fmt::Debug for Sel {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.name())
+ }
+}
+
+impl Ivar {
+ /// Returns the name of self.
+ pub fn name(&self) -> &str {
+ let name = unsafe {
+ CStr::from_ptr(ivar_getName(self))
+ };
+ str::from_utf8(name.to_bytes()).unwrap()
+ }
+
+ /// Returns the offset of self.
+ pub fn offset(&self) -> isize {
+ let offset = unsafe {
+ ivar_getOffset(self)
+ };
+ offset as isize
+ }
+
+ /// Returns the `Encoding` of self.
+ pub fn type_encoding(&self) -> Encoding {
+ let encoding = unsafe {
+ CStr::from_ptr(ivar_getTypeEncoding(self))
+ };
+ let s = str::from_utf8(encoding.to_bytes()).unwrap();
+ encode::from_str(s)
+ }
+}
+
+impl Method {
+ /// Returns the name of self.
+ pub fn name(&self) -> Sel {
+ unsafe {
+ method_getName(self)
+ }
+ }
+
+ /// Returns the `Encoding` of self's return type.
+ pub fn return_type(&self) -> Encoding {
+ unsafe {
+ let encoding = method_copyReturnType(self);
+ encode::from_malloc_str(encoding)
+ }
+ }
+
+ /// Returns the `Encoding` of a single parameter type of self, or
+ /// `None` if self has no parameter at the given index.
+ pub fn argument_type(&self, index: usize) -> Option<Encoding> {
+ unsafe {
+ let encoding = method_copyArgumentType(self, index as c_uint);
+ if encoding.is_null() {
+ None
+ } else {
+ Some(encode::from_malloc_str(encoding))
+ }
+ }
+ }
+
+ /// Returns the number of arguments accepted by self.
+ pub fn arguments_count(&self) -> usize {
+ unsafe {
+ method_getNumberOfArguments(self) as usize
+ }
+ }
+
+ /// Returns the implementation of self.
+ pub fn implementation(&self) -> Imp {
+ unsafe {
+ method_getImplementation(self)
+ }
+ }
+}
+
+impl Class {
+ /// Returns the class definition of a specified class, or `None` if the
+ /// class is not registered with the Objective-C runtime.
+ pub fn get(name: &str) -> Option<&'static Class> {
+ let name = CString::new(name).unwrap();
+ unsafe {
+ let cls = objc_getClass(name.as_ptr());
+ if cls.is_null() { None } else { Some(&*cls) }
+ }
+ }
+
+ /// Obtains the list of registered class definitions.
+ pub fn classes() -> MallocBuffer<&'static Class> {
+ unsafe {
+ let mut count: c_uint = 0;
+ let classes = objc_copyClassList(&mut count);
+ MallocBuffer::new(classes as *mut _, count as usize).unwrap()
+ }
+ }
+
+ /// Returns the total number of registered classes.
+ pub fn classes_count() -> usize {
+ unsafe {
+ objc_getClassList(ptr::null_mut(), 0) as usize
+ }
+ }
+
+ /// Returns the name of self.
+ pub fn name(&self) -> &str {
+ let name = unsafe {
+ CStr::from_ptr(class_getName(self))
+ };
+ str::from_utf8(name.to_bytes()).unwrap()
+ }
+
+ /// Returns the superclass of self, or `None` if self is a root class.
+ pub fn superclass(&self) -> Option<&Class> {
+ unsafe {
+ let superclass = class_getSuperclass(self);
+ if superclass.is_null() { None } else { Some(&*superclass) }
+ }
+ }
+
+ /// Returns the metaclass of self.
+ pub fn metaclass(&self) -> &Class {
+ unsafe {
+ let self_ptr: *const Class = self;
+ &*object_getClass(self_ptr as *const Object)
+ }
+ }
+
+ /// Returns the size of instances of self.
+ pub fn instance_size(&self) -> usize {
+ unsafe {
+ class_getInstanceSize(self) as usize
+ }
+ }
+
+ /// Returns a specified instance method for self, or `None` if self and
+ /// its superclasses do not contain an instance method with the
+ /// specified selector.
+ pub fn instance_method(&self, sel: Sel) -> Option<&Method> {
+ unsafe {
+ let method = class_getInstanceMethod(self, sel);
+ if method.is_null() { None } else { Some(&*method) }
+ }
+ }
+
+ /// Returns the ivar for a specified instance variable of self, or `None`
+ /// if self has no ivar with the given name.
+ pub fn instance_variable(&self, name: &str) -> Option<&Ivar> {
+ let name = CString::new(name).unwrap();
+ unsafe {
+ let ivar = class_getInstanceVariable(self, name.as_ptr());
+ if ivar.is_null() { None } else { Some(&*ivar) }
+ }
+ }
+
+ /// Describes the instance methods implemented by self.
+ pub fn instance_methods(&self) -> MallocBuffer<&Method> {
+ unsafe {
+ let mut count: c_uint = 0;
+ let methods = class_copyMethodList(self, &mut count);
+ MallocBuffer::new(methods as *mut _, count as usize).unwrap()
+ }
+
+ }
+
+ /// Checks whether this class conforms to the specified protocol.
+ pub fn conforms_to(&self, proto: &Protocol) -> bool {
+ unsafe { class_conformsToProtocol(self, proto) == YES }
+ }
+
+ /// Get a list of the protocols to which this class conforms.
+ pub fn adopted_protocols(&self) -> MallocBuffer<&Protocol> {
+ unsafe {
+ let mut count: c_uint = 0;
+ let protos = class_copyProtocolList(self, &mut count);
+ MallocBuffer::new(protos as *mut _, count as usize).unwrap()
+ }
+ }
+
+ /// Describes the instance variables declared by self.
+ pub fn instance_variables(&self) -> MallocBuffer<&Ivar> {
+ unsafe {
+ let mut count: c_uint = 0;
+ let ivars = class_copyIvarList(self, &mut count);
+ MallocBuffer::new(ivars as *mut _, count as usize).unwrap()
+ }
+ }
+}
+
+impl PartialEq for Class {
+ fn eq(&self, other: &Class) -> bool {
+ let self_ptr: *const Class = self;
+ let other_ptr: *const Class = other;
+ self_ptr == other_ptr
+ }
+}
+
+impl Eq for Class { }
+
+impl fmt::Debug for Class {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.name())
+ }
+}
+
+impl Protocol {
+ /// Returns the protocol definition of a specified protocol, or `None` if the
+ /// protocol is not registered with the Objective-C runtime.
+ pub fn get(name: &str) -> Option<&'static Protocol> {
+ let name = CString::new(name).unwrap();
+ unsafe {
+ let proto = objc_getProtocol(name.as_ptr());
+ if proto.is_null() { None } else { Some(&*proto) }
+ }
+ }
+
+ /// Obtains the list of registered protocol definitions.
+ pub fn protocols() -> MallocBuffer<&'static Protocol> {
+ unsafe {
+ let mut count: c_uint = 0;
+ let protocols = objc_copyProtocolList(&mut count);
+ MallocBuffer::new(protocols as *mut _, count as usize).unwrap()
+ }
+ }
+
+ /// Get a list of the protocols to which this protocol conforms.
+ pub fn adopted_protocols(&self) -> MallocBuffer<&Protocol> {
+ unsafe {
+ let mut count: c_uint = 0;
+ let protocols = protocol_copyProtocolList(self, &mut count);
+ MallocBuffer::new(protocols as *mut _, count as usize).unwrap()
+ }
+ }
+
+ /// Checks whether this protocol conforms to the specified protocol.
+ pub fn conforms_to(&self, proto: &Protocol) -> bool {
+ unsafe { protocol_conformsToProtocol(self, proto) == YES }
+ }
+
+ /// Returns the name of self.
+ pub fn name(&self) -> &str {
+ let name = unsafe {
+ CStr::from_ptr(protocol_getName(self))
+ };
+ str::from_utf8(name.to_bytes()).unwrap()
+ }
+}
+
+impl PartialEq for Protocol {
+ fn eq(&self, other: &Protocol) -> bool {
+ unsafe { protocol_isEqual(self, other) == YES }
+ }
+}
+
+impl Eq for Protocol { }
+
+impl fmt::Debug for Protocol {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.name())
+ }
+}
+
+impl Object {
+ /// Returns the class of self.
+ pub fn class(&self) -> &Class {
+ unsafe {
+ &*object_getClass(self)
+ }
+ }
+
+ /// Returns a reference to the ivar of self with the given name.
+ /// Panics if self has no ivar with the given name.
+ /// Unsafe because the caller must ensure that the ivar is actually
+ /// of type `T`.
+ pub unsafe fn get_ivar<T>(&self, name: &str) -> &T where T: Encode {
+ let offset = {
+ let cls = self.class();
+ match cls.instance_variable(name) {
+ Some(ivar) => {
+ assert!(ivar.type_encoding() == T::encode());
+ ivar.offset()
+ }
+ None => panic!("Ivar {} not found on class {:?}", name, cls),
+ }
+ };
+ let ptr = {
+ let self_ptr: *const Object = self;
+ (self_ptr as *const u8).offset(offset) as *const T
+ };
+ &*ptr
+ }
+
+ /// Returns a mutable reference to the ivar of self with the given name.
+ /// Panics if self has no ivar with the given name.
+ /// Unsafe because the caller must ensure that the ivar is actually
+ /// of type `T`.
+ pub unsafe fn get_mut_ivar<T>(&mut self, name: &str) -> &mut T
+ where T: Encode {
+ let offset = {
+ let cls = self.class();
+ match cls.instance_variable(name) {
+ Some(ivar) => {
+ assert!(ivar.type_encoding() == T::encode());
+ ivar.offset()
+ }
+ None => panic!("Ivar {} not found on class {:?}", name, cls),
+ }
+ };
+ let ptr = {
+ let self_ptr: *mut Object = self;
+ (self_ptr as *mut u8).offset(offset) as *mut T
+ };
+ &mut *ptr
+ }
+
+ /// Sets the value of the ivar of self with the given name.
+ /// Panics if self has no ivar with the given name.
+ /// Unsafe because the caller must ensure that the ivar is actually
+ /// of type `T`.
+ pub unsafe fn set_ivar<T>(&mut self, name: &str, value: T)
+ where T: Encode {
+ *self.get_mut_ivar::<T>(name) = value;
+ }
+}
+
+impl fmt::Debug for Object {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "<{:?}: {:p}>", self.class(), self)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use test_utils;
+ use Encode;
+ use super::{Class, Protocol, Sel};
+
+ #[test]
+ fn test_ivar() {
+ let cls = test_utils::custom_class();
+ let ivar = cls.instance_variable("_foo").unwrap();
+ assert!(ivar.name() == "_foo");
+ assert!(ivar.type_encoding() == <u32>::encode());
+ assert!(ivar.offset() > 0);
+
+ let ivars = cls.instance_variables();
+ assert!(ivars.len() > 0);
+ }
+
+ #[test]
+ fn test_method() {
+ let cls = test_utils::custom_class();
+ let sel = Sel::register("foo");
+ let method = cls.instance_method(sel).unwrap();
+ assert!(method.name().name() == "foo");
+ assert!(method.arguments_count() == 2);
+ assert!(method.return_type() == <u32>::encode());
+ assert!(method.argument_type(1).unwrap() == Sel::encode());
+
+ let methods = cls.instance_methods();
+ assert!(methods.len() > 0);
+ }
+
+ #[test]
+ fn test_class() {
+ let cls = test_utils::custom_class();
+ assert!(cls.name() == "CustomObject");
+ assert!(cls.instance_size() > 0);
+ assert!(cls.superclass().is_none());
+
+ assert!(Class::get(cls.name()) == Some(cls));
+
+ let metaclass = cls.metaclass();
+ // The metaclass of a root class is a subclass of the root class
+ assert!(metaclass.superclass().unwrap() == cls);
+
+ let subclass = test_utils::custom_subclass();
+ assert!(subclass.superclass().unwrap() == cls);
+ }
+
+ #[test]
+ fn test_classes() {
+ assert!(Class::classes_count() > 0);
+ let classes = Class::classes();
+ assert!(classes.len() > 0);
+ }
+
+ #[test]
+ fn test_protocol() {
+ let proto = test_utils::custom_protocol();
+ assert!(proto.name() == "CustomProtocol");
+ let class = test_utils::custom_class();
+ assert!(class.conforms_to(proto));
+ let class_protocols = class.adopted_protocols();
+ assert!(class_protocols.len() > 0);
+ }
+
+ #[test]
+ fn test_protocol_method() {
+ let class = test_utils::custom_class();
+ let result: i32 = unsafe {
+ msg_send![class, addNumber:1 toNumber:2]
+ };
+ assert_eq!(result, 3);
+ }
+
+ #[test]
+ fn test_subprotocols() {
+ let sub_proto = test_utils::custom_subprotocol();
+ let super_proto = test_utils::custom_protocol();
+ assert!(sub_proto.conforms_to(super_proto));
+ let adopted_protocols = sub_proto.adopted_protocols();
+ assert_eq!(adopted_protocols[0], super_proto);
+ }
+
+ #[test]
+ fn test_protocols() {
+ // Ensure that a protocol has been registered on linux
+ let _ = test_utils::custom_protocol();
+
+ let protocols = Protocol::protocols();
+ assert!(protocols.len() > 0);
+ }
+
+ #[test]
+ fn test_object() {
+ let mut obj = test_utils::custom_object();
+ assert!(obj.class() == test_utils::custom_class());
+ let result: u32 = unsafe {
+ obj.set_ivar("_foo", 4u32);
+ *obj.get_ivar("_foo")
+ };
+ assert!(result == 4);
+ }
+}
diff --git a/third_party/rust/objc/src/test_utils.rs b/third_party/rust/objc/src/test_utils.rs
new file mode 100644
index 0000000000..873f82f6b1
--- /dev/null
+++ b/third_party/rust/objc/src/test_utils.rs
@@ -0,0 +1,187 @@
+use std::ops::{Deref, DerefMut};
+use std::os::raw::c_char;
+use std::sync::{Once, ONCE_INIT};
+
+use declare::{ClassDecl, ProtocolDecl};
+use runtime::{Class, Object, Protocol, Sel, self};
+use {Encode, Encoding};
+
+pub struct CustomObject {
+ obj: *mut Object,
+}
+
+impl CustomObject {
+ fn new(class: &Class) -> Self {
+ let obj = unsafe {
+ runtime::class_createInstance(class, 0)
+ };
+ CustomObject { obj: obj }
+ }
+}
+
+impl Deref for CustomObject {
+ type Target = Object;
+
+ fn deref(&self) -> &Object {
+ unsafe { &*self.obj }
+ }
+}
+
+impl DerefMut for CustomObject {
+ fn deref_mut(&mut self) -> &mut Object {
+ unsafe { &mut *self.obj }
+ }
+}
+
+impl Drop for CustomObject {
+ fn drop(&mut self) {
+ unsafe {
+ runtime::object_dispose(self.obj);
+ }
+ }
+}
+
+#[derive(Eq, PartialEq)]
+pub struct CustomStruct {
+ pub a: u64,
+ pub b: u64,
+ pub c: u64,
+ pub d: u64,
+}
+
+unsafe impl Encode for CustomStruct {
+ fn encode() -> Encoding {
+ let mut code = "{CustomStruct=".to_owned();
+ for _ in 0..4 {
+ code.push_str(u64::encode().as_str());
+ }
+ code.push_str("}");
+ unsafe {
+ Encoding::from_str(&code)
+ }
+ }
+}
+
+pub fn custom_class() -> &'static Class {
+ static REGISTER_CUSTOM_CLASS: Once = ONCE_INIT;
+
+ REGISTER_CUSTOM_CLASS.call_once(|| {
+ // The runtime will call this method, so it has to be implemented
+ extern fn custom_obj_class_initialize(_this: &Class, _cmd: Sel) { }
+
+ let mut decl = ClassDecl::root("CustomObject", custom_obj_class_initialize).unwrap();
+ let proto = custom_protocol();
+
+ decl.add_protocol(proto);
+ decl.add_ivar::<u32>("_foo");
+
+ extern fn custom_obj_set_foo(this: &mut Object, _cmd: Sel, foo: u32) {
+ unsafe { this.set_ivar::<u32>("_foo", foo); }
+ }
+
+ extern fn custom_obj_get_foo(this: &Object, _cmd: Sel) -> u32 {
+ unsafe { *this.get_ivar::<u32>("_foo") }
+ }
+
+ extern fn custom_obj_get_struct(_this: &Object, _cmd: Sel) -> CustomStruct {
+ CustomStruct { a: 1, b: 2, c: 3, d: 4 }
+ }
+
+ extern fn custom_obj_class_method(_this: &Class, _cmd: Sel) -> u32 {
+ 7
+ }
+
+ extern fn custom_obj_set_bar(this: &mut Object, _cmd: Sel, bar: u32) {
+ unsafe { this.set_ivar::<u32>("_foo", bar) ;}
+ }
+
+ extern fn custom_obj_add_number_to_number(_this: &Class, _cmd: Sel, fst: i32, snd: i32) -> i32 {
+ fst + snd
+ }
+
+ unsafe {
+ let set_foo: extern fn(&mut Object, Sel, u32) = custom_obj_set_foo;
+ decl.add_method(sel!(setFoo:), set_foo);
+ let get_foo: extern fn(&Object, Sel) -> u32 = custom_obj_get_foo;
+ decl.add_method(sel!(foo), get_foo);
+ let get_struct: extern fn(&Object, Sel) -> CustomStruct = custom_obj_get_struct;
+ decl.add_method(sel!(customStruct), get_struct);
+ let class_method: extern fn(&Class, Sel) -> u32 = custom_obj_class_method;
+ decl.add_class_method(sel!(classFoo), class_method);
+
+ let protocol_instance_method: extern fn(&mut Object, Sel, u32) = custom_obj_set_bar;
+ decl.add_method(sel!(setBar:), protocol_instance_method);
+ let protocol_class_method: extern fn(&Class, Sel, i32, i32) -> i32 = custom_obj_add_number_to_number;
+ decl.add_class_method(sel!(addNumber:toNumber:), protocol_class_method);
+ }
+
+ decl.register();
+ });
+
+ class!(CustomObject)
+}
+
+pub fn custom_protocol() -> &'static Protocol {
+ static REGISTER_CUSTOM_PROTOCOL: Once = ONCE_INIT;
+
+ REGISTER_CUSTOM_PROTOCOL.call_once(|| {
+ let mut decl = ProtocolDecl::new("CustomProtocol").unwrap();
+
+ decl.add_method_description::<(i32,), ()>(sel!(setBar:), true);
+ decl.add_method_description::<(), *const c_char>(sel!(getName), false);
+ decl.add_class_method_description::<(i32, i32), i32>(sel!(addNumber:toNumber:), true);
+
+ decl.register();
+ });
+
+ Protocol::get("CustomProtocol").unwrap()
+}
+
+pub fn custom_subprotocol() -> &'static Protocol {
+ static REGISTER_CUSTOM_SUBPROTOCOL: Once = ONCE_INIT;
+
+ REGISTER_CUSTOM_SUBPROTOCOL.call_once(|| {
+ let super_proto = custom_protocol();
+ let mut decl = ProtocolDecl::new("CustomSubProtocol").unwrap();
+
+ decl.add_protocol(super_proto);
+ decl.add_method_description::<(u32,), u32>(sel!(calculateFoo:), true);
+
+ decl.register();
+ });
+
+ Protocol::get("CustomSubProtocol").unwrap()
+}
+
+pub fn custom_object() -> CustomObject {
+ CustomObject::new(custom_class())
+}
+
+pub fn custom_subclass() -> &'static Class {
+ static REGISTER_CUSTOM_SUBCLASS: Once = ONCE_INIT;
+
+ REGISTER_CUSTOM_SUBCLASS.call_once(|| {
+ let superclass = custom_class();
+ let mut decl = ClassDecl::new("CustomSubclassObject", superclass).unwrap();
+
+ extern fn custom_subclass_get_foo(this: &Object, _cmd: Sel) -> u32 {
+ let foo: u32 = unsafe {
+ msg_send![super(this, custom_class()), foo]
+ };
+ foo + 2
+ }
+
+ unsafe {
+ let get_foo: extern fn(&Object, Sel) -> u32 = custom_subclass_get_foo;
+ decl.add_method(sel!(foo), get_foo);
+ }
+
+ decl.register();
+ });
+
+ class!(CustomSubclassObject)
+}
+
+pub fn custom_subclass_object() -> CustomObject {
+ CustomObject::new(custom_subclass())
+}