summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/client/app/src/ui/macos/objc.rs
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/crashreporter/client/app/src/ui/macos/objc.rs')
-rw-r--r--toolkit/crashreporter/client/app/src/ui/macos/objc.rs242
1 files changed, 242 insertions, 0 deletions
diff --git a/toolkit/crashreporter/client/app/src/ui/macos/objc.rs b/toolkit/crashreporter/client/app/src/ui/macos/objc.rs
new file mode 100644
index 0000000000..d4f3f1c419
--- /dev/null
+++ b/toolkit/crashreporter/client/app/src/ui/macos/objc.rs
@@ -0,0 +1,242 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Objective-C bindings and helpers.
+
+// Forward all exports from the `objc` crate.
+pub use objc::*;
+
+/// An objc class instance which contains rust data `T`.
+#[derive(Clone, Copy)]
+#[repr(transparent)]
+pub struct Objc<T> {
+ pub instance: cocoa::id,
+ _phantom: std::marker::PhantomData<*mut T>,
+}
+
+impl<T> Objc<T> {
+ pub fn new(instance: cocoa::id) -> Self {
+ Objc {
+ instance,
+ _phantom: std::marker::PhantomData,
+ }
+ }
+
+ pub fn data(&self) -> &T {
+ let data = *unsafe { (*self.instance).get_ivar::<usize>("rust_self") } as *mut T;
+ unsafe { &*data }
+ }
+
+ pub fn data_mut(&mut self) -> &mut T {
+ let data = *unsafe { (*self.instance).get_ivar::<usize>("rust_self") } as *mut T;
+ unsafe { &mut *data }
+ }
+}
+
+impl<T> std::ops::Deref for Objc<T> {
+ type Target = T;
+ fn deref(&self) -> &Self::Target {
+ self.data()
+ }
+}
+
+impl<T> std::ops::DerefMut for Objc<T> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ self.data_mut()
+ }
+}
+
+unsafe impl<T> Encode for Objc<T> {
+ fn encode() -> Encoding {
+ cocoa::id::encode()
+ }
+}
+
+/// Wrapper to provide `Encode` for bindgen-generated types (bindgen should do this in the future).
+#[repr(transparent)]
+pub struct Ptr<T>(pub T);
+
+unsafe impl<T> Encode for Ptr<T> {
+ fn encode() -> Encoding {
+ cocoa::id::encode()
+ }
+}
+
+/// A strong objective-c reference to `T`.
+#[repr(transparent)]
+pub struct StrongRef<T> {
+ ptr: rc::StrongPtr,
+ _phantom: std::marker::PhantomData<T>,
+}
+
+impl<T> Clone for StrongRef<T> {
+ fn clone(&self) -> Self {
+ StrongRef {
+ ptr: self.ptr.clone(),
+ _phantom: self._phantom,
+ }
+ }
+}
+
+impl<T> std::ops::Deref for StrongRef<T> {
+ type Target = T;
+ fn deref(&self) -> &Self::Target {
+ let obj: &cocoa::id = &*self.ptr;
+ unsafe { std::mem::transmute(obj) }
+ }
+}
+
+impl<T> StrongRef<T> {
+ /// Assume the given pointer-wrapper is an already-retained strong reference.
+ ///
+ /// # Safety
+ /// The type _must_ be the same size as cocoa::id and contain only a cocoa::id.
+ pub unsafe fn new(v: T) -> Self {
+ std::mem::transmute_copy(&v)
+ }
+
+ /// Retain the given pointer-wrapper.
+ ///
+ /// # Safety
+ /// The type _must_ be the same size as cocoa::id and contain only a cocoa::id.
+ #[allow(dead_code)]
+ pub unsafe fn retain(v: T) -> Self {
+ let obj: cocoa::id = std::mem::transmute_copy(&v);
+ StrongRef {
+ ptr: rc::StrongPtr::retain(obj),
+ _phantom: std::marker::PhantomData,
+ }
+ }
+
+ pub fn autorelease(self) -> T {
+ let obj = self.ptr.autorelease();
+ unsafe { std::mem::transmute_copy(&obj) }
+ }
+
+ pub fn weak(&self) -> WeakRef<T> {
+ WeakRef {
+ ptr: self.ptr.weak(),
+ _phantom: std::marker::PhantomData,
+ }
+ }
+
+ /// Unwrap the StrongRef value without affecting reference counts.
+ ///
+ /// This is the opposite of `new`.
+ #[allow(dead_code)]
+ pub fn unwrap(self: Self) -> T {
+ let v = unsafe { std::mem::transmute_copy(&self) };
+ std::mem::forget(self);
+ v
+ }
+
+ /// Cast to a base class.
+ ///
+ /// Bindgen pointer-wrappers have trival `From<Derived> for Base` implementations.
+ pub fn cast<U: From<T>>(self) -> StrongRef<U> {
+ StrongRef {
+ ptr: self.ptr,
+ _phantom: std::marker::PhantomData,
+ }
+ }
+}
+
+/// A weak objective-c reference to `T`.
+#[derive(Clone)]
+#[repr(transparent)]
+pub struct WeakRef<T> {
+ ptr: rc::WeakPtr,
+ _phantom: std::marker::PhantomData<T>,
+}
+
+impl<T> WeakRef<T> {
+ pub fn lock(&self) -> Option<StrongRef<T>> {
+ let ptr = self.ptr.load();
+ if ptr.is_null() {
+ None
+ } else {
+ Some(StrongRef {
+ ptr,
+ _phantom: std::marker::PhantomData,
+ })
+ }
+ }
+}
+
+/// A macro for creating an objc class.
+///
+/// Classes _must_ be registered before use (`Objc<T>::register()`).
+///
+/// Example:
+/// ```
+/// struct Foo(u8);
+///
+/// objc_class! {
+/// impl Foo: NSObject {
+/// #[sel(mySelector:)]
+/// fn my_selector(&mut self, arg: u8) -> u8 {
+/// self.0 + arg
+/// }
+/// }
+/// }
+///
+/// fn make_foo() -> StrongRef<Objc<Foo>> {
+/// Foo(42).into_object()
+/// }
+/// ```
+///
+/// Call `T::into_object()` to create the objective-c class instance.
+macro_rules! objc_class {
+ ( impl $name:ident : $base:ident $(<$($protocol:ident),+>)? {
+ $(
+ #[sel($($sel:tt)+)]
+ fn $mname:ident (&mut $self:ident $(, $argname:ident : $argtype:ty )*) $(-> $rettype:ty)? $body:block
+ )*
+ }) => {
+ impl Objc<$name> {
+ pub fn register() {
+ let mut decl = declare::ClassDecl::new(concat!("CR", stringify!($name)), class!($base)).expect(concat!("failed to declare ", stringify!($name), " class"));
+ $($(decl.add_protocol(runtime::Protocol::get(stringify!($protocol)).expect(concat!("failed to find ",stringify!($protocol)," protocol")));)+)?
+ decl.add_ivar::<usize>("rust_self");
+ $({
+ extern fn method_impl(obj: &mut runtime::Object, _: runtime::Sel $(, $argname: $argtype )*) $(-> $rettype)? {
+ Objc::<$name>::new(obj).$mname($($argname),*)
+ }
+ unsafe {
+ decl.add_method(sel!($($sel)+), method_impl as extern fn(&mut runtime::Object, runtime::Sel $(, $argname: $argtype )*) $(-> $rettype)?);
+ }
+ })*
+ {
+ extern fn dealloc_impl(obj: &runtime::Object, _: runtime::Sel) {
+ drop(unsafe { Box::from_raw(*obj.get_ivar::<usize>("rust_self") as *mut $name) });
+ unsafe {
+ let _: () = msg_send![super(obj, class!(NSObject)), dealloc];
+ }
+ }
+ unsafe {
+ decl.add_method(sel!(dealloc), dealloc_impl as extern fn(&runtime::Object, runtime::Sel));
+ }
+ }
+ decl.register();
+ }
+
+ pub fn class() -> &'static runtime::Class {
+ runtime::Class::get(concat!("CR", stringify!($name))).expect("class not registered")
+ }
+
+ $(fn $mname (&mut $self $(, $argname : $argtype )*) $(-> $rettype)? $body)*
+ }
+
+ impl $name {
+ pub fn into_object(self) -> StrongRef<Objc<$name>> {
+ let obj: *mut runtime::Object = unsafe { msg_send![Objc::<Self>::class(), alloc] };
+ unsafe { (*obj).set_ivar("rust_self", Box::into_raw(Box::new(self)) as usize) };
+ let obj: *mut runtime::Object = unsafe { msg_send![obj, init] };
+ unsafe { StrongRef::new(Objc::new(obj)) }
+ }
+ }
+ }
+}
+
+pub(crate) use objc_class;