Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Foundation ownership fixes #40

Merged
merged 17 commits into from
Nov 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions objc2/src/declare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,12 @@ pub trait MethodImplementation {

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)* {
impl<$s, $r, $($t),*> MethodImplementation for $f
where
$s: Message,
$r: Encode,
$($t: Encode,)*
{
type Callee = $s;
type Ret = $r;
type Args = ($($t,)*);
Expand All @@ -68,8 +72,8 @@ macro_rules! method_decl_impl {
}
);
($($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!(-T, R, extern "C" fn(&T, Sel $(, $t)*) -> R, $($t),*);
method_decl_impl!(-T, R, extern "C" fn(&mut T, Sel $(, $t)*) -> R, $($t),*);
);
}

Expand Down
45 changes: 21 additions & 24 deletions objc2/src/rc/autorelease.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,23 @@ impl AutoreleasePool {
Self { context }
}

/// This will be removed in a future version.
#[cfg_attr(
all(debug_assertions, not(feature = "unstable_autoreleasesafe")),
inline
)]
#[doc(hidden)]
pub fn __verify_is_inner(&self) {
#[cfg(all(debug_assertions, not(feature = "unstable_autoreleasesafe")))]
POOLS.with(|c| {
assert_eq!(
c.borrow().last(),
Some(&self.context),
"Tried to use lifetime from pool that was not innermost"
)
});
}

/// Returns a shared reference to the given autoreleased pointer object.
///
/// This is the preferred way to make references from autoreleased
Expand All @@ -70,20 +87,10 @@ impl AutoreleasePool {
///
/// This is equivalent to `&*ptr`, and shares the unsafety of that, except
/// the lifetime is bound to the pool instead of being unbounded.
#[cfg_attr(
all(debug_assertions, not(feature = "unstable_autoreleasesafe")),
inline
)]
#[inline]
#[allow(clippy::needless_lifetimes)]
pub unsafe fn ptr_as_ref<'p, T>(&'p self, ptr: *const T) -> &'p T {
#[cfg(all(debug_assertions, not(feature = "unstable_autoreleasesafe")))]
POOLS.with(|c| {
assert_eq!(
c.borrow().last(),
Some(&self.context),
"Tried to create shared reference with a lifetime from a pool that was not the innermost pool"
)
});
self.__verify_is_inner();
// SAFETY: Checked by the caller
&*ptr
}
Expand All @@ -100,21 +107,11 @@ impl AutoreleasePool {
///
/// This is equivalent to `&mut *ptr`, and shares the unsafety of that,
/// except the lifetime is bound to the pool instead of being unbounded.
#[cfg_attr(
all(debug_assertions, not(feature = "unstable_autoreleasesafe")),
inline
)]
#[inline]
#[allow(clippy::needless_lifetimes)]
#[allow(clippy::mut_from_ref)]
pub unsafe fn ptr_as_mut<'p, T>(&'p self, ptr: *mut T) -> &'p mut T {
#[cfg(all(debug_assertions, not(feature = "unstable_autoreleasesafe")))]
POOLS.with(|c| {
assert_eq!(
c.borrow().last(),
Some(&self.context),
"Tried to create unique reference with a lifetime from a pool that was not the innermost pool")
}
);
self.__verify_is_inner();
// SAFETY: Checked by the caller
&mut *ptr
}
Expand Down
25 changes: 12 additions & 13 deletions objc2_block/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,19 +231,24 @@ macro_rules! concrete_block_impl {
);
($f:ident, $($a:ident : $t:ident),*) => (
impl<$($t: Encode,)* R: Encode, X> IntoConcreteBlock<($($t,)*)> for X
where X: Fn($($t,)*) -> R {
where
X: Fn($($t,)*) -> R,
{
type Ret = R;

fn into_concrete_block(self) -> ConcreteBlock<($($t,)*), R, X> {
unsafe extern fn $f<$($t,)* R, X>(
block_ptr: *mut ConcreteBlock<($($t,)*), R, X>
$(, $a: $t)*) -> R
where X: Fn($($t,)*) -> R {
block_ptr: *mut ConcreteBlock<($($t,)*), R, X>
$(, $a: $t)*
) -> R
where
X: Fn($($t,)*) -> R
{
let block = &*block_ptr;
(block.closure)($($a),*)
}

let f: unsafe extern fn(*mut ConcreteBlock<($($t,)*), R, X> $(, $a: $t)*) -> R = $f;
let f: unsafe extern "C" fn(*mut ConcreteBlock<($($t,)*), R, X> $(, $a: $t)*) -> R = $f;
unsafe {
ConcreteBlock::with_invoke(mem::transmute(f), self)
}
Expand Down Expand Up @@ -397,10 +402,7 @@ impl<A, R, F> ConcreteBlock<A, R, F> {
}
}

impl<A, R, F> ConcreteBlock<A, R, F>
where
F: 'static,
{
impl<A, R, F: 'static> ConcreteBlock<A, R, F> {
/// Copy self onto the heap as an `RcBlock`.
pub fn copy(self) -> RcBlock<A, R> {
unsafe {
Expand All @@ -415,10 +417,7 @@ where
}
}

impl<A, R, F> Clone for ConcreteBlock<A, R, F>
where
F: Clone,
{
impl<A, R, F: Clone> Clone for ConcreteBlock<A, R, F> {
fn clone(&self) -> Self {
unsafe {
ConcreteBlock::with_invoke(mem::transmute(self.base.invoke), self.closure.clone())
Expand Down
19 changes: 11 additions & 8 deletions objc2_foundation/examples/basic_usage.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use objc2::rc::autoreleasepool;
use objc2_foundation::{
INSArray, INSCopying, INSDictionary, INSObject, INSString, NSArray, NSDictionary, NSObject,
NSString,
INSArray, INSCopying, INSDictionary, INSString, NSArray, NSDictionary, NSObject, NSString,
};

fn main() {
Expand All @@ -17,22 +17,25 @@ fn main() {
for obj in array.object_enumerator() {
println!("{:?}", obj);
}
println!("{}", array.count());
println!("{}", array.len());

// Turn the NSArray back into a Vec
let mut objs = NSArray::into_vec(array);
let obj = objs.pop().unwrap();

// Create an NSString from a str slice
let string = NSString::from_str("Hello, world!");
println!("{}", string.as_str());
let string2 = string.copy();
println!("{}", string2.as_str());
// Use an autoreleasepool to get the `str` contents of the NSString
autoreleasepool(|pool| {
println!("{}", string.as_str(pool));
let string2 = string.copy();
println!("{}", string2.as_str(pool));
});

// Create a dictionary mapping strings to objects
let keys = &[&*string];
let vals = vec![obj];
let dict = NSDictionary::from_keys_and_objects(keys, vals);
println!("{:?}", dict.object_for(&string));
println!("{}", dict.count());
println!("{:?}", dict.get(&string));
println!("{}", dict.len());
}
104 changes: 104 additions & 0 deletions objc2_foundation/examples/class_with_lifetime.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use std::marker::PhantomData;
use std::ptr::NonNull;
use std::sync::Once;

use objc2::declare::ClassDecl;
use objc2::rc::{Id, Owned, Shared};
use objc2::runtime::{Class, Object, Sel};
use objc2::{msg_send, sel};
use objc2::{Encoding, Message, RefEncode};
use objc2_foundation::{INSObject, NSObject};

#[repr(C)]
pub struct MyObject<'a> {
_priv: [u8; 0],
// `init` defaults ivars to all zeroes, so allow for that here
// TODO: Verify this claim!
p: PhantomData<Option<&'a mut u8>>,
}

unsafe impl RefEncode for MyObject<'_> {
const ENCODING_REF: Encoding<'static> = Encoding::Object;
}

unsafe impl Message for MyObject<'_> {}

impl<'a> MyObject<'a> {
fn new(number_ptr: &'a mut u8) -> Id<Self, Owned> {
unsafe {
let obj: *mut Self = msg_send![Self::class(), alloc];
let obj: *mut Self = msg_send![obj, initWithPtr: number_ptr];
Id::new(NonNull::new_unchecked(obj))
}
}

fn get(&self) -> Option<&'a u8> {
unsafe {
let obj = &*(self as *const _ as *const Object);
*obj.get_ivar("_number_ptr")
}
}

fn write(&mut self, number: u8) {
let ptr: &mut Option<&'a mut u8> = unsafe {
let obj = &mut *(self as *mut _ as *mut Object);
obj.get_mut_ivar("_number_ptr")
};
if let Some(ptr) = ptr {
**ptr = number;
}
}
}

static MYOBJECT_REGISTER_CLASS: Once = Once::new();

unsafe impl INSObject for MyObject<'_> {
type Ownership = Owned;

fn class() -> &'static Class {
MYOBJECT_REGISTER_CLASS.call_once(|| {
let superclass = NSObject::class();
let mut decl = ClassDecl::new("MyObject", superclass).unwrap();
decl.add_ivar::<&mut u8>("_number_ptr");

extern "C" fn init_with_ptr(this: &mut Object, _cmd: Sel, ptr: *mut u8) -> *mut Object {
unsafe {
this.set_ivar("_number_ptr", ptr);
}
this
}

unsafe {
let init_with_ptr: extern "C" fn(&mut Object, Sel, *mut u8) -> *mut Object =
init_with_ptr;
decl.add_method(sel!(initWithPtr:), init_with_ptr);
}

decl.register();
});

Class::get("MyObject").unwrap()
}
}

fn main() {
let mut number = 54;
let mut obj = MyObject::new(&mut number);

println!("Number: {}", obj.get().unwrap());

obj.write(7);
// Won't compile, since `obj` holds a mutable reference to number
// println!("Number: {}", number);
println!("Number: {}", obj.get().unwrap());

let obj: Id<_, Shared> = obj.into();
let obj2 = obj.clone();

println!("Number: {}", obj.get().unwrap());
println!("Number: {}", obj2.get().unwrap());

drop(obj);
drop(obj2);
println!("Number: {}", number);
}
11 changes: 10 additions & 1 deletion objc2_foundation/examples/custom_class.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::ptr::NonNull;
use std::sync::Once;

use objc2::declare::ClassDecl;
use objc2::rc::{Id, Owned};
use objc2::runtime::{Class, Object, Sel};
use objc2::{msg_send, sel};
use objc2::{Encoding, Message, RefEncode};
Expand All @@ -21,6 +23,11 @@ unsafe impl RefEncode for MYObject {
}

impl MYObject {
fn new() -> Id<Self, <Self as INSObject>::Ownership> {
let cls = Self::class();
unsafe { Id::new(NonNull::new_unchecked(msg_send![cls, new])) }
}

fn number(&self) -> u32 {
unsafe {
let obj = &*(self as *const _ as *const Object);
Expand All @@ -40,7 +47,9 @@ unsafe impl Message for MYObject {}

static MYOBJECT_REGISTER_CLASS: Once = Once::new();

impl INSObject for MYObject {
unsafe impl INSObject for MYObject {
type Ownership = Owned;

fn class() -> &'static Class {
MYOBJECT_REGISTER_CLASS.call_once(|| {
let superclass = NSObject::class();
Expand Down
Loading