Skip to content

Commit fb21bfb

Browse files
committed
Automatically generate MainThreadMarker argument in methods
1 parent ee5cd91 commit fb21bfb

File tree

11 files changed

+182
-52
lines changed

11 files changed

+182
-52
lines changed

crates/header-translator/src/cache.rs

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::BTreeMap;
1+
use std::collections::{BTreeMap, BTreeSet};
22
use std::mem;
33

44
use crate::config::Config;
@@ -7,16 +7,38 @@ use crate::id::ItemIdentifier;
77
use crate::method::Method;
88
use crate::output::Output;
99
use crate::stmt::Stmt;
10+
use crate::Mutability;
1011

1112
/// A helper struct for doing global analysis on the output.
1213
#[derive(Debug, PartialEq, Clone)]
1314
pub struct Cache<'a> {
1415
config: &'a Config,
16+
mainthreadonly_classes: BTreeSet<ItemIdentifier>,
1517
}
1618

1719
impl<'a> Cache<'a> {
18-
pub fn new(_output: &Output, config: &'a Config) -> Self {
19-
Self { config }
20+
pub fn new(output: &Output, config: &'a Config) -> Self {
21+
let mut mainthreadonly_classes = BTreeSet::new();
22+
23+
for library in output.libraries.values() {
24+
for file in library.files.values() {
25+
for stmt in file.stmts.iter() {
26+
if let Stmt::ClassDecl {
27+
id,
28+
mutability: Mutability::MainThreadOnly,
29+
..
30+
} = stmt
31+
{
32+
mainthreadonly_classes.insert(id.clone());
33+
}
34+
}
35+
}
36+
}
37+
38+
Self {
39+
config,
40+
mainthreadonly_classes,
41+
}
2042
}
2143

2244
pub fn update(&self, output: &mut Output) {
@@ -68,6 +90,57 @@ impl<'a> Cache<'a> {
6890
}
6991
}
7092

93+
// Add `mainthreadonly` to relevant methods
94+
for stmt in file.stmts.iter_mut() {
95+
match stmt {
96+
Stmt::Methods {
97+
cls: id, methods, ..
98+
}
99+
| Stmt::ProtocolDecl { id, methods, .. } => {
100+
for method in methods.iter_mut() {
101+
let mut result_type_contains_mainthreadonly: bool = false;
102+
method.result_type.visit_required_types(&mut |id| {
103+
if self.mainthreadonly_classes.contains(id) {
104+
result_type_contains_mainthreadonly = true;
105+
}
106+
});
107+
108+
match (method.is_class, self.mainthreadonly_classes.contains(id)) {
109+
// MainThreadOnly class with static method; assume the method is main thread only
110+
(true, true) => {
111+
result_type_contains_mainthreadonly = true;
112+
}
113+
// Class with non-static method; do the normal check
114+
(true, false) => {}
115+
// MainThreadOnly class with static method; skip the check
116+
(false, true) => {
117+
continue;
118+
}
119+
// Class with non-static method; do the normal check
120+
(false, false) => {}
121+
}
122+
123+
if !result_type_contains_mainthreadonly {
124+
continue;
125+
}
126+
127+
let mut any_argument_contains_mainthreadonly: bool = false;
128+
for (_, argument) in method.arguments.iter() {
129+
argument.visit_toplevel_types(&mut |id| {
130+
if self.mainthreadonly_classes.contains(id) {
131+
any_argument_contains_mainthreadonly = true;
132+
}
133+
});
134+
}
135+
if !any_argument_contains_mainthreadonly {
136+
method.mainthreadonly = true;
137+
}
138+
}
139+
}
140+
_ => {}
141+
}
142+
}
143+
71144
// Fix up a few typedef + enum declarations
72145
let mut iter = mem::take(&mut file.stmts).into_iter().peekable();
73146
while let Some(stmt) = iter.next() {

crates/header-translator/src/id.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,14 @@ impl ItemIdentifier {
123123
self.library == "Foundation" && self.name == "NSComparator"
124124
}
125125

126+
pub fn main_thread_marker() -> Self {
127+
Self {
128+
name: "NSThread".to_string(),
129+
library: "Foundation".to_string(),
130+
file_name: Some("NSThread".to_string()),
131+
}
132+
}
133+
126134
pub fn feature(&self) -> Option<impl fmt::Display + '_> {
127135
struct ItemIdentifierFeature<'a>(&'a ItemIdentifier);
128136

crates/header-translator/src/method.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -249,14 +249,14 @@ pub struct Method {
249249
pub is_class: bool,
250250
is_optional_protocol: bool,
251251
memory_management: MemoryManagement,
252-
arguments: Vec<(String, Ty)>,
252+
pub(crate) arguments: Vec<(String, Ty)>,
253253
pub result_type: Ty,
254254
safe: bool,
255255
mutating: bool,
256256
is_protocol: bool,
257257
// Thread-safe, even on main-thread only (@MainActor/@UIActor) classes
258258
non_isolated: bool,
259-
mainthreadonly: bool,
259+
pub(crate) mainthreadonly: bool,
260260
}
261261

262262
impl Method {
@@ -349,6 +349,10 @@ impl Method {
349349
}
350350

351351
self.result_type.visit_required_types(&mut f);
352+
353+
if self.mainthreadonly {
354+
f(&ItemIdentifier::main_thread_marker())
355+
}
352356
}
353357
}
354358

@@ -636,6 +640,11 @@ impl fmt::Display for Method {
636640
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
637641
let _span = debug_span!("method", self.fn_name).entered();
638642

643+
// TODO: Use this somehow?
644+
// if self.non_isolated {
645+
// writeln!(f, "// non_isolated")?;
646+
// }
647+
639648
//
640649
// Attributes
641650
//
@@ -689,7 +698,11 @@ impl fmt::Display for Method {
689698
// Arguments
690699
for (param, arg_ty) in &self.arguments {
691700
let param = handle_reserved(&crate::to_snake_case(param));
692-
write!(f, "{param}: {arg_ty},")?;
701+
write!(f, "{param}: {arg_ty}, ")?;
702+
}
703+
// FIXME: Skipping main thread only on protocols for now
704+
if self.mainthreadonly && !self.is_protocol {
705+
write!(f, "mtm: MainThreadMarker")?;
693706
}
694707
write!(f, ")")?;
695708

crates/header-translator/src/rust_type.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,12 @@ impl IdType {
345345
}
346346
}
347347
}
348+
349+
fn visit_toplevel_types(&self, f: &mut impl FnMut(&ItemIdentifier)) {
350+
if let Some(id) = self._id() {
351+
f(id);
352+
}
353+
}
348354
}
349355

350356
impl fmt::Display for IdType {
@@ -1036,6 +1042,25 @@ impl Inner {
10361042
_ => {}
10371043
}
10381044
}
1045+
1046+
pub fn visit_toplevel_types(&self, f: &mut impl FnMut(&ItemIdentifier)) {
1047+
match self {
1048+
Self::Id { ty, .. } => {
1049+
ty.visit_toplevel_types(f);
1050+
}
1051+
Self::Pointer {
1052+
// Only visit non-null types
1053+
nullability: Nullability::NonNull,
1054+
is_const: _,
1055+
pointee,
1056+
} => {
1057+
pointee.visit_toplevel_types(f);
1058+
}
1059+
// TODO
1060+
Self::TypeDef { id } => f(id),
1061+
_ => {}
1062+
}
1063+
}
10391064
}
10401065

10411066
/// This is sound to output in (almost, c_void is not a valid return type) any
@@ -1492,6 +1517,14 @@ impl Ty {
14921517

14931518
self.ty.visit_required_types(f);
14941519
}
1520+
1521+
pub fn visit_toplevel_types(&self, f: &mut impl FnMut(&ItemIdentifier)) {
1522+
if let TyKind::MethodReturn { with_error: true } = &self.kind {
1523+
f(&ItemIdentifier::nserror());
1524+
}
1525+
1526+
self.ty.visit_toplevel_types(f);
1527+
}
14951528
}
14961529

14971530
impl Ty {

crates/icrate/Cargo.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,9 @@ unstable-example-basic_usage = [
131131
unstable-example-delegate = [
132132
"apple",
133133
"Foundation",
134-
"Foundation_NSString",
135134
"Foundation_NSNotification",
135+
"Foundation_NSString",
136+
"Foundation_NSThread",
136137
"AppKit",
137138
"AppKit_NSApplication",
138139
]
@@ -165,6 +166,7 @@ unstable-example-browser = [
165166
"Foundation",
166167
"Foundation_NSNotification",
167168
"Foundation_NSString",
169+
"Foundation_NSThread",
168170
"Foundation_NSURL",
169171
"Foundation_NSURLRequest",
170172
"WebKit",
@@ -177,10 +179,11 @@ unstable-example-metal = [
177179
"AppKit_NSWindow",
178180
"Foundation",
179181
"Foundation_NSCoder",
182+
"Foundation_NSDate",
180183
"Foundation_NSError",
181184
"Foundation_NSNotification",
182185
"Foundation_NSString",
183-
"Foundation_NSDate",
186+
"Foundation_NSThread",
184187
"Metal",
185188
"Metal_MTLCompileOptions",
186189
"Metal_MTLRenderPassDescriptor",

0 commit comments

Comments
 (0)