Skip to content

Commit de14f52

Browse files
authored
Merge pull request #445 from madsmtm/improve-new
Improve `new` and `init` method handling
2 parents dd6c804 + 1090ca2 commit de14f52

33 files changed

+907
-975
lines changed

crates/header-translator/src/cache.rs

Lines changed: 3 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -1,99 +1,22 @@
11
use std::collections::BTreeMap;
22
use std::mem;
33

4-
use crate::availability::Availability;
5-
use crate::config::{ClassData, Config};
4+
use crate::config::Config;
65
use crate::file::File;
76
use crate::id::ItemIdentifier;
87
use crate::method::Method;
98
use crate::output::Output;
109
use crate::stmt::Stmt;
1110

12-
#[derive(Debug, PartialEq, Clone)]
13-
struct MethodCache {
14-
availability: Availability,
15-
methods: Vec<Method>,
16-
category: ItemIdentifier<Option<String>>,
17-
}
18-
19-
#[derive(Debug, PartialEq, Clone, Default)]
20-
struct ClassCache {
21-
/// Methods that should be duplicated onto any subclass.
22-
to_emit: Vec<MethodCache>,
23-
// We don't need availability here, since a superclass' availability
24-
// should always be greater than the subclass'.
25-
}
26-
27-
impl ClassCache {
28-
fn all_methods_data(&self) -> impl Iterator<Item = (bool, &str)> {
29-
self.to_emit
30-
.iter()
31-
.flat_map(|cache| cache.methods.iter().map(|m| m.id()))
32-
}
33-
}
34-
3511
/// A helper struct for doing global analysis on the output.
3612
#[derive(Debug, PartialEq, Clone)]
3713
pub struct Cache<'a> {
38-
classes: BTreeMap<ItemIdentifier, ClassCache>,
3914
config: &'a Config,
4015
}
4116

4217
impl<'a> Cache<'a> {
43-
pub fn new(output: &Output, config: &'a Config) -> Self {
44-
let mut classes: BTreeMap<_, ClassCache> = BTreeMap::new();
45-
46-
for (name, library) in &output.libraries {
47-
let _span = debug_span!("library", name).entered();
48-
for (name, file) in &library.files {
49-
let _span = debug_span!("file", name).entered();
50-
for stmt in &file.stmts {
51-
if let Some((cls, method_cache)) = Self::cache_stmt(stmt) {
52-
let cache = classes.entry(cls.clone()).or_default();
53-
cache.to_emit.push(method_cache);
54-
}
55-
}
56-
}
57-
}
58-
59-
Self { classes, config }
60-
}
61-
62-
fn cache_stmt(stmt: &Stmt) -> Option<(&ItemIdentifier, MethodCache)> {
63-
if let Stmt::Methods {
64-
cls,
65-
generics: _,
66-
category,
67-
availability,
68-
superclasses: _,
69-
methods,
70-
description,
71-
} = stmt
72-
{
73-
let _span = debug_span!("Stmt::Methods", ?cls).entered();
74-
let methods: Vec<Method> = methods
75-
.iter()
76-
.filter(|method| method.emit_on_subclasses())
77-
.cloned()
78-
.collect();
79-
if methods.is_empty() {
80-
return None;
81-
}
82-
if description.is_some() {
83-
warn!(description, "description was set");
84-
}
85-
let category = category.clone().with_new_path(cls);
86-
Some((
87-
cls,
88-
MethodCache {
89-
availability: availability.clone(),
90-
methods,
91-
category,
92-
},
93-
))
94-
} else {
95-
None
96-
}
18+
pub fn new(_output: &Output, config: &'a Config) -> Self {
19+
Self { config }
9720
}
9821

9922
pub fn update(&self, output: &mut Output) {
@@ -145,70 +68,6 @@ impl<'a> Cache<'a> {
14568
}
14669
}
14770

148-
let mut new_stmts = Vec::new();
149-
for stmt in &mut file.stmts {
150-
#[allow(clippy::single_match)] // There will be others
151-
match stmt {
152-
Stmt::ClassDecl {
153-
id,
154-
generics,
155-
superclasses,
156-
..
157-
} => {
158-
let _span = debug_span!("Stmt::ClassDecl", ?id).entered();
159-
let data = self.config.class_data.get(&id.name);
160-
161-
// Used for duplicate checking (sometimes the subclass
162-
// defines the same method that the superclass did).
163-
let mut seen_methods: Vec<_> = self
164-
.classes
165-
.get(id)
166-
.map(|cache| cache.all_methods_data())
167-
.into_iter()
168-
.flatten()
169-
.collect();
170-
171-
for (superclass, _) in &*superclasses {
172-
if let Some(cache) = self.classes.get(superclass) {
173-
new_stmts.extend(cache.to_emit.iter().filter_map(|cache| {
174-
let methods: Vec<_> = cache
175-
.methods
176-
.iter()
177-
.filter(|method| !seen_methods.contains(&method.id()))
178-
.filter_map(|method| {
179-
method.clone().update(ClassData::get_method_data(
180-
data,
181-
&method.fn_name,
182-
))
183-
})
184-
.collect();
185-
if methods.is_empty() {
186-
return None;
187-
}
188-
189-
Some(Stmt::Methods {
190-
cls: id.clone(),
191-
generics: generics.clone(),
192-
category: cache.category.clone(),
193-
availability: cache.availability.clone(),
194-
superclasses: superclasses.clone(),
195-
methods,
196-
description: Some(format!(
197-
"Methods declared on superclass `{}`",
198-
superclass.name
199-
)),
200-
})
201-
}));
202-
203-
seen_methods.extend(cache.all_methods_data());
204-
}
205-
}
206-
}
207-
_ => {}
208-
}
209-
}
210-
file.stmts.extend(new_stmts);
211-
21271
// Fix up a few typedef + enum declarations
21372
let mut iter = mem::take(&mut file.stmts).into_iter().peekable();
21473
while let Some(stmt) = iter.next() {

crates/header-translator/src/config.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,17 @@ pub struct MethodData {
157157
pub mutating: Option<bool>,
158158
}
159159

160+
impl MethodData {
161+
pub(crate) fn merge_with_superclass(self, superclass: Self) -> Self {
162+
Self {
163+
// Only use `unsafe` from itself, never take if from the superclass
164+
unsafe_: self.unsafe_,
165+
skipped: self.skipped | superclass.skipped,
166+
mutating: self.mutating.or(superclass.mutating),
167+
}
168+
}
169+
}
170+
160171
#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
161172
#[serde(deny_unknown_fields)]
162173
pub struct FnData {

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ data! {
33
// and returning mutable references to those would be unsound - hence
44
// `NSArray` cannot be mutable.
55
class NSArray: ImmutableWithMutableSubclass<Foundation::NSMutableArray> {
6+
unsafe -init;
67
unsafe -count;
78
}
89

910
class NSMutableArray: MutableWithImmutableSuperclass<Foundation::NSArray> {
11+
unsafe -init;
1012
unsafe -removeAllObjects;
1113
}
1214

@@ -28,6 +30,7 @@ data! {
2830
}
2931

3032
class NSMutableString: MutableWithImmutableSuperclass<Foundation::NSString> {
33+
unsafe -init;
3134
unsafe -initWithCapacity;
3235
unsafe +stringWithCapacity;
3336
unsafe -initWithString;
@@ -43,13 +46,15 @@ data! {
4346
class NSConstantString: Immutable {}
4447

4548
class NSAttributedString: ImmutableWithMutableSubclass<Foundation::NSMutableAttributedString> {
49+
unsafe -init;
4650
unsafe -initWithString;
4751
unsafe -initWithAttributedString;
4852
unsafe -string;
4953
unsafe -length;
5054
}
5155

5256
class NSMutableAttributedString: MutableWithImmutableSuperclass<Foundation::NSAttributedString> {
57+
unsafe -init;
5358
unsafe -initWithString;
5459
unsafe -initWithAttributedString;
5560
unsafe -setAttributedString;
@@ -61,13 +66,15 @@ data! {
6166
}
6267

6368
class NSData: ImmutableWithMutableSubclass<Foundation::NSMutableData> {
69+
unsafe -init;
6470
unsafe -initWithData;
6571
unsafe +dataWithData;
6672
unsafe -length;
6773
unsafe -bytes;
6874
}
6975

7076
class NSMutableData: MutableWithImmutableSuperclass<Foundation::NSData> {
77+
unsafe -init;
7178
unsafe +dataWithData;
7279
unsafe -initWithCapacity;
7380
unsafe +dataWithCapacity;
@@ -81,10 +88,12 @@ data! {
8188
class NSPurgeableData: Mutable {}
8289

8390
class NSDictionary: ImmutableWithMutableSubclass<Foundation::NSMutableDictionary> {
91+
unsafe -init;
8492
unsafe -count;
8593
}
8694

8795
class NSMutableDictionary: MutableWithImmutableSuperclass<Foundation::NSDictionary> {
96+
unsafe -init;
8897
unsafe -removeObjectForKey;
8998
unsafe -removeAllObjects;
9099
}
@@ -103,6 +112,7 @@ data! {
103112
}
104113

105114
class NSLock {
115+
unsafe -init;
106116
unsafe -name;
107117
unsafe -setName;
108118
}
@@ -120,6 +130,7 @@ data! {
120130
}
121131

122132
class NSThread {
133+
unsafe -init;
123134
unsafe +currentThread;
124135
unsafe +mainThread;
125136
unsafe -name;
@@ -135,10 +146,12 @@ data! {
135146
}
136147

137148
class NSSet: ImmutableWithMutableSubclass<Foundation::NSMutableSet> {
149+
unsafe -init;
138150
unsafe -count;
139151
}
140152

141153
class NSMutableSet: MutableWithImmutableSuperclass<Foundation::NSSet> {
154+
unsafe -init;
142155
unsafe -removeAllObjects;
143156
}
144157

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,41 @@ macro_rules! __data_methods {
152152
(
153153
@($data:expr)
154154
) => {};
155+
// Mark init (and by extension, `new`) method as safe
156+
(
157+
@($data:expr)
158+
159+
unsafe -init;
160+
161+
$($rest:tt)*
162+
) => {
163+
let mut method_data = $data.methods.entry("init".to_string()).or_default();
164+
method_data.unsafe_ = false;
165+
166+
let mut method_data = $data.methods.entry("new".to_string()).or_default();
167+
method_data.unsafe_ = false;
168+
169+
__data_methods! {
170+
@($data)
171+
$($rest)*
172+
}
173+
};
174+
// Mark new (and by extension, `init`) method as safe
175+
(
176+
@($data:expr)
177+
178+
unsafe +new;
179+
180+
$($rest:tt)*
181+
) => {
182+
__data_methods! {
183+
@($data)
184+
185+
unsafe -init;
186+
187+
$($rest)*
188+
}
189+
};
155190
// Mark method as safe
156191
(
157192
@($data:expr)

crates/header-translator/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ fn main() -> Result<(), BoxError> {
178178
}
179179
writeln!(cargo_toml, "]")?;
180180
}
181+
drop(cargo_toml);
181182
drop(span);
182183

183184
let _span = info_span!("formatting").entered();

crates/header-translator/src/method.rs

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,12 @@ pub struct Method {
242242

243243
impl Method {
244244
/// Value that uniquely identifies the method in a class.
245-
pub fn id(&self) -> (bool, &str) {
246-
(self.is_class, &self.selector)
245+
pub fn id(&self) -> (bool, String) {
246+
(self.is_class, self.selector.clone())
247+
}
248+
249+
pub(crate) fn usable_in_default_id(&self) -> bool {
250+
self.selector == "new" && self.is_class && self.arguments.is_empty() && self.safe
247251
}
248252

249253
fn parent_type_data(entity: &Entity<'_>, context: &Context<'_>) -> (bool, bool) {
@@ -320,17 +324,6 @@ impl Method {
320324
}
321325
}
322326

323-
pub fn update(mut self, data: MethodData) -> Option<Self> {
324-
if data.skipped {
325-
return None;
326-
}
327-
328-
self.mutating = data.mutating.unwrap_or(false);
329-
self.safe = !data.unsafe_;
330-
331-
Some(self)
332-
}
333-
334327
pub fn visit_required_types(&self, mut f: impl FnMut(&ItemIdentifier)) {
335328
for (_, arg) in &self.arguments {
336329
arg.visit_required_types(&mut f);
@@ -432,7 +425,8 @@ impl<'tu> PartialMethod<'tu> {
432425
}
433426

434427
let result_type = entity.get_result_type().expect("method return type");
435-
let mut result_type = Ty::parse_method_return(result_type, context);
428+
let default_nonnull = (selector == "init" && !is_class) || (selector == "new" && is_class);
429+
let mut result_type = Ty::parse_method_return(result_type, default_nonnull, context);
436430

437431
let memory_management = MemoryManagement::new(is_class, &selector, &result_type, modifiers);
438432

@@ -590,10 +584,9 @@ impl Method {
590584
return false;
591585
}
592586
if self.is_class {
593-
!matches!(&*self.selector, "new" | "supportsSecureCoding")
587+
true
594588
} else {
595589
self.memory_management == MemoryManagement::IdInit
596-
&& !matches!(&*self.selector, "init" | "initWithCoder:")
597590
}
598591
}
599592
}

0 commit comments

Comments
 (0)