Skip to content

Commit 486daca

Browse files
authored
Merge pull request #443 from madsmtm/fix-enumeration
Fix enumeration
2 parents 890edc1 + 5d4b25b commit 486daca

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+4625
-436
lines changed

Cargo.lock

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/header-translator/src/data/Foundation.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,28 @@ data! {
1212
unsafe -removeAllObjects;
1313
}
1414

15+
// SAFETY: `NSEnumerator` and subclasses are safe as mutable because even
16+
// though the items it contains are not mutable, the enumerator itself is
17+
// (and it is important that the methods below are marked `&mut` as well).
18+
//
19+
// However, instances of this are only safe for others to create if
20+
// they're ready to pass ownership to the enumerator, or if they somehow
21+
// add a lifetime parameter (to prevent the original collection from
22+
// being modified).
23+
//
24+
// So e.g. `Id<NSMutableArray<T>> -> Id<NSEnumerator<T>>` is safe, as is
25+
// `&Id<NSArray<T: IsCloneable>> -> Id<NSEnumerator<T>>`, and so is
26+
// `&'a NSArray<T: IsCloneable> -> Id<NSEnumerator<T>> + 'a`.
27+
class NSEnumerator: Mutable {
28+
// SAFETY: This removes the object from the internal collection, so it
29+
// may safely return `Id<T>`.
30+
unsafe -nextObject;
31+
// SAFETY: The objects are removed from the internal collection and as
32+
// such are safe to give ownership over.
33+
unsafe -allObjects;
34+
}
35+
class NSDirectoryEnumerator: Mutable {}
36+
1537
class NSString: ImmutableWithMutableSubclass<Foundation::NSMutableString> {
1638
unsafe -init;
1739
unsafe -compare;

crates/header-translator/translation-config.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,10 @@ skipped = true
574574
[struct.NSDecimal]
575575
skipped = true
576576

577+
# Uses `c_ulong` which means we need to specify the encoding manually.
578+
[struct.NSFastEnumerationState]
579+
skipped = true
580+
577581
# Uses stuff from core Darwin libraries which we have not yet mapped
578582
[class.NSAppleEventDescriptor.methods]
579583
descriptorWithDescriptorType_bytes_length = { skipped = true }
@@ -1551,6 +1555,8 @@ definition-skipped = true
15511555
definition-skipped = true
15521556
[class.NSMutableOrderedSet]
15531557
definition-skipped = true
1558+
[class.NSEnumerator]
1559+
definition-skipped = true
15541560

15551561
# These protocol impls would return the wrong types
15561562
[class.NSSimpleCString]

crates/icrate/CHANGELOG.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
3131
* Added `MainThreadMarker::alloc` for allocating objects that need to be so on
3232
the main thread.
3333
* Added automatically generated `new`/`init` methods for all types.
34+
* Added `FromIterator` impls for various collection types.
3435

3536
### Changed
3637
* **BREAKING**: Renamed the `from_slice` method on `NSArray`, `NSSet`,
@@ -45,6 +46,46 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
4546
* **BREAKING**: Renamed `NSMutableCopying::mutable_copy` to `::mutableCopy`.
4647
* **BREAKING**: The default value for `NSUUID` was changed from a nil UUID to
4748
a new random UUID.
49+
* **BREAKING**: Changed how iteration works.
50+
51+
Instead of the single `NSFastEnumerator`, we now have concrete types
52+
`array::Iter`, `array::IterMut`, `array::IterRetained` and
53+
`array::IntoIter`, which allows iterating over `NSArray` in different ways.
54+
55+
Combined with proper `IntoIterator` implementations for collection types,
56+
you can now do:
57+
```rust
58+
let mut array: Id<NSMutableArray<T>> = ...;
59+
60+
for item in &array {
61+
// item: &T
62+
}
63+
64+
// If T: IsMutable
65+
for item in &mut array {
66+
// item: &mut T
67+
}
68+
69+
// If T: IsIdCloneable
70+
for item in array.iter_retained() {
71+
// item: Id<T>
72+
}
73+
74+
for item in array {
75+
// item: Id<T>
76+
}
77+
```
78+
79+
(similar functionality exist for `NSSet` and `NSDictionary`).
80+
* **BREAKING**: Renamed `NSDictionary` methods:
81+
- `keys` -> `keys_vec`.
82+
- `values` -> `values_vec`.
83+
- `values_mut` -> `values_vec_mut`.
84+
- `keys_and_objects` -> `to_vecs`.
85+
- `iter_keys` -> `keys`.
86+
- `iter_values` -> `values`.
87+
* **BREAKING**: `NSDictionary::keys_retained` and
88+
`NSDictionary::values_retained` now return an iterator instead.
4889

4990
### Removed
5091
* **BREAKING**: Removed various redundant `NSProxy` methods.
@@ -56,6 +97,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
5697
* **BREAKING**: Removed a few `init` methods on subclasses that were declared
5798
on categories on their superclass. These should be re-added at some point.
5899

100+
### Fixed
101+
* Soundness issues with enumeration / iteration over collection types.
102+
59103

60104
## icrate 0.0.2 - 2023-02-07
61105

crates/icrate/src/Foundation/additions/array.rs

Lines changed: 126 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1+
//! Utilities for the `NSArray` and `NSMutableArray` classes.
12
#![cfg(feature = "Foundation_NSArray")]
23
use alloc::vec::Vec;
34
use core::fmt;
45
use core::mem;
56
use core::ops::{Index, IndexMut, Range};
67
use core::panic::{RefUnwindSafe, UnwindSafe};
78

8-
use objc2::msg_send;
99
use objc2::mutability::{IsMutable, IsRetainable};
10+
use objc2::rc::IdFromIterator;
1011

12+
use super::iter;
1113
use super::util;
1214
use crate::common::*;
1315
#[cfg(feature = "Foundation_NSMutableArray")]
@@ -220,15 +222,6 @@ extern_methods!(
220222
);
221223

222224
impl<T: Message> NSArray<T> {
223-
#[doc(alias = "objectEnumerator")]
224-
#[cfg(feature = "Foundation_NSEnumerator")]
225-
pub fn iter(&self) -> Foundation::NSEnumerator2<'_, T> {
226-
unsafe {
227-
let result: *mut Object = msg_send![self, objectEnumerator];
228-
Foundation::NSEnumerator2::from_ptr(result)
229-
}
230-
}
231-
232225
unsafe fn objects_in_range_unchecked(&self, range: Range<usize>) -> Vec<&T> {
233226
let range = Foundation::NSRange::from(range);
234227
let mut vec: Vec<NonNull<T>> = Vec::with_capacity(range.length);
@@ -340,33 +333,109 @@ impl<T: Message> NSMutableArray<T> {
340333
}
341334
}
342335

343-
unsafe impl<T: Message> Foundation::NSFastEnumeration2 for NSArray<T> {
336+
impl<T: Message> NSArray<T> {
337+
#[doc(alias = "objectEnumerator")]
338+
#[inline]
339+
pub fn iter(&self) -> Iter<'_, T> {
340+
Iter(super::iter::Iter::new(self))
341+
}
342+
343+
#[doc(alias = "objectEnumerator")]
344+
#[inline]
345+
pub fn iter_mut(&mut self) -> IterMut<'_, T>
346+
where
347+
T: IsMutable,
348+
{
349+
IterMut(super::iter::IterMut::new(self))
350+
}
351+
352+
#[doc(alias = "objectEnumerator")]
353+
#[inline]
354+
pub fn iter_retained(&self) -> IterRetained<'_, T>
355+
where
356+
T: IsIdCloneable,
357+
{
358+
IterRetained(super::iter::IterRetained::new(self))
359+
}
360+
}
361+
362+
unsafe impl<T: Message> iter::FastEnumerationHelper for NSArray<T> {
344363
type Item = T;
364+
365+
#[inline]
366+
fn maybe_len(&self) -> Option<usize> {
367+
Some(self.len())
368+
}
345369
}
346370

347371
#[cfg(feature = "Foundation_NSMutableArray")]
348-
unsafe impl<T: Message> Foundation::NSFastEnumeration2 for NSMutableArray<T> {
372+
unsafe impl<T: Message> iter::FastEnumerationHelper for NSMutableArray<T> {
349373
type Item = T;
374+
375+
#[inline]
376+
fn maybe_len(&self) -> Option<usize> {
377+
Some(self.len())
378+
}
350379
}
351380

352-
impl<'a, T: Message> IntoIterator for &'a NSArray<T> {
353-
type Item = &'a T;
354-
type IntoIter = Foundation::NSFastEnumerator2<'a, NSArray<T>>;
381+
/// An iterator over the items of a `NSArray`.
382+
#[derive(Debug)]
383+
pub struct Iter<'a, T: Message>(iter::Iter<'a, NSArray<T>>);
355384

356-
fn into_iter(self) -> Self::IntoIter {
357-
use Foundation::NSFastEnumeration2;
358-
self.iter_fast()
359-
}
385+
__impl_iter! {
386+
impl<'a, T: Message> Iterator<Item = &'a T> for Iter<'a, T> { ... }
360387
}
361388

362-
#[cfg(feature = "Foundation_NSMutableArray")]
363-
impl<'a, T: Message> IntoIterator for &'a NSMutableArray<T> {
364-
type Item = &'a T;
365-
type IntoIter = Foundation::NSFastEnumerator2<'a, NSMutableArray<T>>;
389+
/// A mutable iterator over the items of a `NSArray`.
390+
#[derive(Debug)]
391+
pub struct IterMut<'a, T: Message>(iter::IterMut<'a, NSArray<T>>);
366392

367-
fn into_iter(self) -> Self::IntoIter {
368-
use Foundation::NSFastEnumeration2;
369-
self.iter_fast()
393+
__impl_iter! {
394+
impl<'a, T: IsMutable> Iterator<Item = &'a mut T> for IterMut<'a, T> { ... }
395+
}
396+
397+
/// An iterator that retains the items of a `NSArray`.
398+
#[derive(Debug)]
399+
pub struct IterRetained<'a, T: Message>(iter::IterRetained<'a, NSArray<T>>);
400+
401+
__impl_iter! {
402+
impl<'a, T: IsIdCloneable> Iterator<Item = Id<T>> for IterRetained<'a, T> { ... }
403+
}
404+
405+
/// A consuming iterator over the items of a `NSArray`.
406+
#[derive(Debug)]
407+
pub struct IntoIter<T: Message>(iter::IntoIter<NSArray<T>>);
408+
409+
__impl_iter! {
410+
impl<'a, T: Message> Iterator<Item = Id<T>> for IntoIter<T> { ... }
411+
}
412+
413+
__impl_into_iter! {
414+
impl<T: Message> IntoIterator for &NSArray<T> {
415+
type IntoIter = Iter<'_, T>;
416+
}
417+
418+
#[cfg(feature = "Foundation_NSMutableArray")]
419+
impl<T: Message> IntoIterator for &NSMutableArray<T> {
420+
type IntoIter = Iter<'_, T>;
421+
}
422+
423+
impl<T: IsMutable> IntoIterator for &mut NSArray<T> {
424+
type IntoIter = IterMut<'_, T>;
425+
}
426+
427+
#[cfg(feature = "Foundation_NSMutableArray")]
428+
impl<T: IsMutable> IntoIterator for &mut NSMutableArray<T> {
429+
type IntoIter = IterMut<'_, T>;
430+
}
431+
432+
impl<T: IsIdCloneable> IntoIterator for Id<NSArray<T>> {
433+
type IntoIter = IntoIter<T>;
434+
}
435+
436+
#[cfg(feature = "Foundation_NSMutableArray")]
437+
impl<T: Message> IntoIterator for Id<NSMutableArray<T>> {
438+
type IntoIter = IntoIter<T>;
370439
}
371440
}
372441

@@ -403,8 +472,7 @@ impl<T: IsMutable> IndexMut<usize> for NSMutableArray<T> {
403472
impl<T: fmt::Debug + Message> fmt::Debug for NSArray<T> {
404473
#[inline]
405474
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
406-
use Foundation::NSFastEnumeration2;
407-
f.debug_list().entries(self.iter_fast()).finish()
475+
f.debug_list().entries(self).finish()
408476
}
409477
}
410478

@@ -424,3 +492,33 @@ impl<'a, T: IsRetainable> Extend<&'a T> for NSMutableArray<T> {
424492
.for_each(move |item| unsafe { self.addObject(item) })
425493
}
426494
}
495+
496+
impl<'a, T: IsRetainable + 'a> IdFromIterator<&'a T> for NSArray<T> {
497+
fn id_from_iter<I: IntoIterator<Item = &'a T>>(iter: I) -> Id<Self> {
498+
let vec = Vec::from_iter(iter);
499+
Self::from_slice(&vec)
500+
}
501+
}
502+
503+
impl<T: Message> IdFromIterator<Id<T>> for NSArray<T> {
504+
fn id_from_iter<I: IntoIterator<Item = Id<T>>>(iter: I) -> Id<Self> {
505+
let vec = Vec::from_iter(iter);
506+
Self::from_vec(vec)
507+
}
508+
}
509+
510+
#[cfg(feature = "Foundation_NSMutableArray")]
511+
impl<'a, T: IsRetainable + 'a> IdFromIterator<&'a T> for NSMutableArray<T> {
512+
fn id_from_iter<I: IntoIterator<Item = &'a T>>(iter: I) -> Id<Self> {
513+
let vec = Vec::from_iter(iter);
514+
Self::from_slice(&vec)
515+
}
516+
}
517+
518+
#[cfg(feature = "Foundation_NSMutableArray")]
519+
impl<T: Message> IdFromIterator<Id<T>> for NSMutableArray<T> {
520+
fn id_from_iter<I: IntoIterator<Item = Id<T>>>(iter: I) -> Id<Self> {
521+
let vec = Vec::from_iter(iter);
522+
Self::from_vec(vec)
523+
}
524+
}

crates/icrate/src/Foundation/additions/data.rs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ use core::ops::{IndexMut, Range};
88
use core::panic::{RefUnwindSafe, UnwindSafe};
99
use core::slice::{self, SliceIndex};
1010

11+
#[cfg(feature = "block")]
12+
use objc2::rc::IdFromIterator;
13+
1114
use crate::common::*;
1215
#[cfg(feature = "Foundation_NSMutableData")]
1316
use crate::Foundation::NSMutableData;
@@ -246,18 +249,22 @@ impl std::io::Write for NSMutableData {
246249
}
247250
}
248251

249-
// #[cfg(feature = "Foundation_NSMutableData")]
250-
// impl FromIterator<u8> for Id<NSMutableData> {
251-
// fn from_iter<T: IntoIterator<Item = u8>>(iter: T) -> Self {
252-
// let iter = iter.into_iter();
253-
// let (lower, _) = iter.size_hint();
254-
// let data = Self::with_capacity(lower);
255-
// for item in iter {
256-
// data.push(item);
257-
// }
258-
// data
259-
// }
260-
// }
252+
#[cfg(feature = "block")]
253+
impl IdFromIterator<u8> for NSData {
254+
fn id_from_iter<I: IntoIterator<Item = u8>>(iter: I) -> Id<Self> {
255+
let vec = Vec::from_iter(iter);
256+
Self::from_vec(vec)
257+
}
258+
}
259+
260+
#[cfg(feature = "Foundation_NSMutableData")]
261+
#[cfg(feature = "block")]
262+
impl IdFromIterator<u8> for NSMutableData {
263+
fn id_from_iter<I: IntoIterator<Item = u8>>(iter: I) -> Id<Self> {
264+
let vec = Vec::from_iter(iter);
265+
Self::from_vec(vec)
266+
}
267+
}
261268

262269
#[cfg(feature = "block")]
263270
unsafe fn with_vec<T: Message>(obj: Option<Allocated<T>>, bytes: Vec<u8>) -> Id<T> {

0 commit comments

Comments
 (0)