Skip to content

Commit 53e3cc4

Browse files
committed
Automatically generate MainThreadMarker argument in methods
1 parent a897b42 commit 53e3cc4

File tree

12 files changed

+195
-54
lines changed

12 files changed

+195
-54
lines changed

crates/header-translator/src/cache.rs

Lines changed: 86 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,67 @@ 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
110+
(true, true) => {
111+
// Assume the method needs main thread
112+
result_type_contains_mainthreadonly = true;
113+
}
114+
// Class with static method
115+
(true, false) => {
116+
// Continue with the normal check
117+
}
118+
// MainThreadOnly class with non-static method
119+
(false, true) => {
120+
// Method is already required to run on main
121+
// thread, so no need to add MainThreadMarker
122+
continue;
123+
}
124+
// Class with non-static method
125+
(false, false) => {
126+
// Continue with the normal check
127+
}
128+
}
129+
130+
if result_type_contains_mainthreadonly {
131+
let mut any_argument_contains_mainthreadonly: bool = false;
132+
for (_, argument) in method.arguments.iter() {
133+
// Important: We only visit the top-level types, to not
134+
// include e.g. `Option<&NSView>` or `&NSArray<NSView>`.
135+
argument.visit_toplevel_types(&mut |id| {
136+
if self.mainthreadonly_classes.contains(id) {
137+
any_argument_contains_mainthreadonly = true;
138+
}
139+
});
140+
}
141+
142+
// Apply main thread only, unless a (required)
143+
// argument was main thread only.
144+
if !any_argument_contains_mainthreadonly {
145+
method.mainthreadonly = true;
146+
}
147+
}
148+
}
149+
}
150+
_ => {}
151+
}
152+
}
153+
71154
// Fix up a few typedef + enum declarations
72155
let mut iter = mem::take(&mut file.stmts).into_iter().peekable();
73156
while let Some(stmt) = iter.next() {

crates/header-translator/src/method.rs

Lines changed: 12 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 {
@@ -636,6 +636,11 @@ impl fmt::Display for Method {
636636
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
637637
let _span = debug_span!("method", self.fn_name).entered();
638638

639+
// TODO: Use this somehow?
640+
// if self.non_isolated {
641+
// writeln!(f, "// non_isolated")?;
642+
// }
643+
639644
//
640645
// Attributes
641646
//
@@ -689,7 +694,11 @@ impl fmt::Display for Method {
689694
// Arguments
690695
for (param, arg_ty) in &self.arguments {
691696
let param = handle_reserved(&crate::to_snake_case(param));
692-
write!(f, "{param}: {arg_ty},")?;
697+
write!(f, "{param}: {arg_ty}, ")?;
698+
}
699+
// FIXME: Skipping main thread only on protocols for now
700+
if self.mainthreadonly && !self.is_protocol {
701+
write!(f, "mtm: MainThreadMarker")?;
693702
}
694703
write!(f, ")")?;
695704

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",

crates/icrate/examples/browser.rs

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ use icrate::{
1212
NSWindowStyleMaskResizable, NSWindowStyleMaskTitled,
1313
},
1414
Foundation::{
15-
ns_string, NSNotification, NSObject, NSObjectProtocol, NSPoint, NSRect, NSSize,
16-
NSURLRequest, NSURL,
15+
ns_string, MainThreadMarker, NSNotification, NSObject, NSObjectProtocol, NSPoint, NSRect,
16+
NSSize, NSURLRequest, NSURL,
1717
},
1818
WebKit::{WKNavigation, WKNavigationDelegate, WKWebView},
1919
};
2020
use objc2::{
2121
declare::{Ivar, IvarDrop},
2222
declare_class, msg_send, msg_send_id,
23-
mutability::InteriorMutable,
23+
mutability::MainThreadOnly,
2424
rc::Id,
2525
runtime::{AnyObject, ProtocolObject, Sel},
2626
sel, ClassType,
@@ -47,7 +47,7 @@ declare_class!(
4747

4848
unsafe impl ClassType for Delegate {
4949
type Super = NSObject;
50-
type Mutability = InteriorMutable;
50+
type Mutability = MainThreadOnly;
5151
const NAME: &'static str = "Delegate";
5252
}
5353

@@ -68,9 +68,9 @@ declare_class!(
6868
#[method(applicationDidFinishLaunching:)]
6969
#[allow(non_snake_case)]
7070
unsafe fn applicationDidFinishLaunching(&self, _notification: &NSNotification) {
71+
let mtm = MainThreadMarker::from(self);
7172
// create the app window
7273
let window = {
73-
let this = NSWindow::alloc();
7474
let content_rect = NSRect::new(NSPoint::new(0., 0.), NSSize::new(1024., 768.));
7575
let style = NSWindowStyleMaskClosable
7676
| NSWindowStyleMaskResizable
@@ -79,7 +79,7 @@ declare_class!(
7979
let flag = false;
8080
unsafe {
8181
NSWindow::initWithContentRect_styleMask_backing_defer(
82-
this,
82+
mtm.alloc(),
8383
content_rect,
8484
style,
8585
backing_store_type,
@@ -90,16 +90,14 @@ declare_class!(
9090

9191
// create the web view
9292
let web_view = {
93-
let this = WKWebView::alloc();
9493
let frame_rect = NSRect::ZERO;
95-
unsafe { WKWebView::initWithFrame(this, frame_rect) }
94+
unsafe { WKWebView::initWithFrame(mtm.alloc(), frame_rect) }
9695
};
9796

9897
// create the nav bar view
9998
let nav_bar = {
10099
let frame_rect = NSRect::ZERO;
101-
let this = NSStackView::alloc();
102-
let this = unsafe { NSStackView::initWithFrame(this, frame_rect) };
100+
let this = unsafe { NSStackView::initWithFrame(mtm.alloc(), frame_rect) };
103101
unsafe {
104102
this.setOrientation(NSUserInterfaceLayoutOrientationHorizontal);
105103
this.setAlignment(NSLayoutAttributeHeight);
@@ -112,8 +110,7 @@ declare_class!(
112110
// create the nav buttons view
113111
let nav_buttons = {
114112
let frame_rect = NSRect::ZERO;
115-
let this = NSStackView::alloc();
116-
let this = unsafe { NSStackView::initWithFrame(this, frame_rect) };
113+
let this = unsafe { NSStackView::initWithFrame(mtm.alloc(), frame_rect) };
117114
unsafe {
118115
this.setOrientation(NSUserInterfaceLayoutOrientationHorizontal);
119116
this.setAlignment(NSLayoutAttributeHeight);
@@ -130,7 +127,7 @@ declare_class!(
130127
let target = Some::<&AnyObject>(&web_view);
131128
let action = Some(sel!(goBack));
132129
let this =
133-
unsafe { NSButton::buttonWithTitle_target_action(title, target, action) };
130+
unsafe { NSButton::buttonWithTitle_target_action(title, target, action, mtm) };
134131
unsafe { this.setBezelStyle(NSBezelStyleShadowlessSquare) };
135132
this
136133
};
@@ -142,7 +139,7 @@ declare_class!(
142139
let target = Some::<&AnyObject>(&web_view);
143140
let action = Some(sel!(goForward));
144141
let this =
145-
unsafe { NSButton::buttonWithTitle_target_action(title, target, action) };
142+
unsafe { NSButton::buttonWithTitle_target_action(title, target, action, mtm) };
146143
unsafe { this.setBezelStyle(NSBezelStyleShadowlessSquare) };
147144
this
148145
};
@@ -155,8 +152,7 @@ declare_class!(
155152
// create the url text field
156153
let nav_url = {
157154
let frame_rect = NSRect::ZERO;
158-
let this = NSTextField::alloc();
159-
let this = unsafe { NSTextField::initWithFrame(this, frame_rect) };
155+
let this = unsafe { NSTextField::initWithFrame(mtm.alloc(), frame_rect) };
160156
unsafe {
161157
this.setDrawsBackground(true);
162158
this.setBackgroundColor(Some(&NSColor::lightGrayColor()));
@@ -173,8 +169,7 @@ declare_class!(
173169
// create the window content view
174170
let content_view = {
175171
let frame_rect = unsafe { window.frame() };
176-
let this = NSStackView::alloc();
177-
let this = unsafe { NSStackView::initWithFrame(this, frame_rect) };
172+
let this = unsafe { NSStackView::initWithFrame(mtm.alloc(), frame_rect) };
178173
unsafe {
179174
this.setOrientation(NSUserInterfaceLayoutOrientationVertical);
180175
this.setAlignment(NSLayoutAttributeWidth);
@@ -217,7 +212,7 @@ declare_class!(
217212
menu_app_item.setSubmenu(Some(&menu_app_menu));
218213
menu.addItem(&menu_app_item);
219214

220-
let app = NSApplication::sharedApplication();
215+
let app = NSApplication::sharedApplication(mtm);
221216
app.setMainMenu(Some(&menu));
222217
}
223218

@@ -286,19 +281,20 @@ declare_class!(
286281
);
287282

288283
impl Delegate {
289-
pub fn new() -> Id<Self> {
290-
unsafe { msg_send_id![Self::alloc(), init] }
284+
pub fn new(mtm: MainThreadMarker) -> Id<Self> {
285+
unsafe { msg_send_id![mtm.alloc(), init] }
291286
}
292287
}
293288

294289
unsafe impl NSObjectProtocol for Delegate {}
295290

296291
fn main() {
297-
let app = unsafe { NSApplication::sharedApplication() };
292+
let mtm = MainThreadMarker::new().unwrap();
293+
let app = unsafe { NSApplication::sharedApplication(mtm) };
298294
unsafe { app.setActivationPolicy(NSApplicationActivationPolicyRegular) };
299295

300296
// initialize the delegate
301-
let delegate = Delegate::new();
297+
let delegate = Delegate::new(mtm);
302298

303299
// configure the application delegate
304300
unsafe {

0 commit comments

Comments
 (0)