Skip to content

Commit

Permalink
Parse sendability/mainthread attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Sep 1, 2023
1 parent bffd0db commit 5fa7a76
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 37 deletions.
1 change: 1 addition & 0 deletions crates/header-translator/src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ impl<'a> Cache<'a> {
ty: enum_ty,
kind: _,
variants: _,
sendable: _,
}) = iter.peek_mut()
{
if enum_ty.is_typedef_to(&id.name) {
Expand Down
47 changes: 43 additions & 4 deletions crates/header-translator/src/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ struct MethodModifiers {
returns_retained: bool,
returns_not_retained: bool,
designated_initializer: bool,
non_isolated: bool,
sendable: Option<bool>,
mainthreadonly: bool,
}

impl MethodModifiers {
Expand All @@ -68,6 +71,18 @@ impl MethodModifiers {
UnexposedAttr::ReturnsNotRetained => {
this.returns_not_retained = true;
}
UnexposedAttr::NonIsolated => {
this.non_isolated = true;
}
UnexposedAttr::Sendable => {
this.sendable = Some(true);
}
UnexposedAttr::NonSendable => {
this.sendable = Some(false);
}
UnexposedAttr::UIActor => {
this.mainthreadonly = true;
}
attr => error!(?attr, "unknown attribute"),
}
}
Expand Down Expand Up @@ -214,6 +229,7 @@ impl MemoryManagement {
consumes_self: false,
returns_retained: false,
returns_not_retained: false,
..
} = modifiers
{
Self::Normal
Expand All @@ -238,6 +254,9 @@ pub struct Method {
safe: bool,
mutating: bool,
is_protocol: bool,
// Thread-safe, even on main-thread only (@MainActor/@UIActor) classes
non_isolated: bool,
mainthreadonly: bool,
}

impl Method {
Expand Down Expand Up @@ -367,6 +386,10 @@ impl<'tu> PartialMethod<'tu> {

let modifiers = MethodModifiers::parse(&entity, context);

if modifiers.sendable.is_some() {
error!("sendable on method");
}

let mut arguments: Vec<_> = entity
.get_arguments()
.expect("method arguments")
Expand All @@ -377,6 +400,7 @@ impl<'tu> PartialMethod<'tu> {
let qualifier = entity
.get_objc_qualifiers()
.map(MethodArgumentQualifier::parse);
let mut sendable = None;

immediate_children(&entity, |entity, _span| match entity.get_kind() {
EntityKind::ObjCClassRef
Expand All @@ -390,7 +414,11 @@ impl<'tu> PartialMethod<'tu> {
}
EntityKind::UnexposedAttr => {
if let Some(attr) = UnexposedAttr::parse(&entity, context) {
error!(?attr, "unknown attribute");
match attr {
UnexposedAttr::Sendable => sendable = Some(true),
UnexposedAttr::NonSendable => sendable = Some(false),
attr => error!(?attr, "unknown attribute"),
}
}
}
// For some reason we recurse into array types
Expand All @@ -399,7 +427,7 @@ impl<'tu> PartialMethod<'tu> {
});

let ty = entity.get_type().expect("argument type");
let ty = Ty::parse_method_argument(ty, qualifier, context);
let ty = Ty::parse_method_argument(ty, qualifier, sendable, context);

(name, ty)
})
Expand Down Expand Up @@ -463,6 +491,8 @@ impl<'tu> PartialMethod<'tu> {
// immutable subclass, or as a property.
mutating: data.mutating.unwrap_or(parent_is_mutable),
is_protocol,
non_isolated: modifiers.non_isolated,
mainthreadonly: modifiers.mainthreadonly,
},
))
}
Expand Down Expand Up @@ -519,6 +549,7 @@ impl PartialProperty<'_> {
let ty = Ty::parse_property_return(
entity.get_type().expect("property type"),
is_copy,
modifiers.sendable,
context,
);

Expand All @@ -538,6 +569,8 @@ impl PartialProperty<'_> {
// is, so let's default to immutable.
mutating: getter_data.mutating.unwrap_or(false),
is_protocol,
non_isolated: modifiers.non_isolated,
mainthreadonly: modifiers.mainthreadonly,
})
} else {
None
Expand All @@ -546,8 +579,12 @@ impl PartialProperty<'_> {
let setter = if let Some(setter_name) = setter_name {
let setter_data = setter_data.expect("setter_data must be present if setter_name was");
if !setter_data.skipped {
let ty =
Ty::parse_property(entity.get_type().expect("property type"), is_copy, context);
let ty = Ty::parse_property(
entity.get_type().expect("property type"),
is_copy,
modifiers.sendable,
context,
);

let selector = setter_name.clone() + ":";
let memory_management =
Expand All @@ -566,6 +603,8 @@ impl PartialProperty<'_> {
// Setters are usually mutable if the class itself is.
mutating: setter_data.mutating.unwrap_or(parent_is_mutable),
is_protocol,
non_isolated: modifiers.non_isolated,
mainthreadonly: modifiers.mainthreadonly,
})
} else {
None
Expand Down
63 changes: 51 additions & 12 deletions crates/header-translator/src/rust_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -532,10 +532,30 @@ impl Inner {

let unexposed_nullability = if let TypeKind::Unexposed = ty.get_kind() {
let nullability = ty.get_nullability();
attributed_name = parse_unexposed_tokens(&attributed_name);
let (new_attributed_name, attributed_attr) = parse_unexposed_tokens(&attributed_name);
// Also parse the expected name to ensure that the formatting that
// TokenStream does is the same on both.
name = parse_unexposed_tokens(&name);
let (new_name, attr) = parse_unexposed_tokens(&name);
if attributed_attr != attr {
error!(
?attributed_attr,
?attr,
"attributed attr was not equal to attr",
);
}

match attr {
Some(UnexposedAttr::NonIsolated | UnexposedAttr::UIActor) => {
// Ignored for now; these are almost always also emitted on the method/property,
// which is where they will be useful in any case.
}
Some(attr) => error!(?attr, "unknown attribute"),
None => {}
}

attributed_name = new_attributed_name;
name = new_name;

ty = ty
.get_modified_type()
.expect("attributed type to have modified type");
Expand Down Expand Up @@ -1193,6 +1213,7 @@ impl Ty {
pub fn parse_method_argument(
ty: Type<'_>,
_qualifier: Option<MethodArgumentQualifier>,
_sendable: Option<bool>,
context: &Context<'_>,
) -> Self {
let ty = Inner::parse(ty, Lifetime::Unspecified, context);
Expand Down Expand Up @@ -1253,7 +1274,7 @@ impl Ty {
}

pub fn parse_function_argument(ty: Type<'_>, context: &Context<'_>) -> Self {
let mut this = Self::parse_method_argument(ty, None, context);
let mut this = Self::parse_method_argument(ty, None, None, context);
this.kind = TyKind::FnArgument;
this
}
Expand Down Expand Up @@ -1304,6 +1325,7 @@ impl Ty {
ty: Type<'_>,
// Ignored; see `parse_property_return`
_is_copy: bool,
_sendable: Option<bool>,
context: &Context<'_>,
) -> Self {
let ty = Inner::parse(ty, Lifetime::Unspecified, context);
Expand All @@ -1320,7 +1342,12 @@ impl Ty {
}
}

pub fn parse_property_return(ty: Type<'_>, is_copy: bool, context: &Context<'_>) -> Self {
pub fn parse_property_return(
ty: Type<'_>,
is_copy: bool,
_sendable: Option<bool>,
context: &Context<'_>,
) -> Self {
let mut ty = Inner::parse(ty, Lifetime::Unspecified, context);

// `@property(copy)` is expected to always return a nonnull instance
Expand Down Expand Up @@ -1736,10 +1763,10 @@ impl fmt::Display for Ty {
/// - NS_SWIFT_UNAVAILABLE
/// - NS_REFINED_FOR_SWIFT
/// - ...
fn parse_unexposed_tokens(s: &str) -> String {
fn parse_unexposed_tokens(s: &str) -> (String, Option<UnexposedAttr>) {
let tokens = TokenStream::from_str(s).expect("parse attributed name");
let mut iter = tokens.into_iter().peekable();
if let Some(TokenTree::Ident(ident)) = iter.peek() {
let attr = if let Some(TokenTree::Ident(ident)) = iter.peek() {
let ident = ident.to_string();
if let Ok(attr) = UnexposedAttr::from_name(&ident, || {
iter.next();
Expand All @@ -1750,13 +1777,15 @@ fn parse_unexposed_tokens(s: &str) -> String {
None
}
}) {
if let Some(attr) = attr {
error!(?attr, "unknown attribute");
}
iter.next();
attr
} else {
None
}
}
TokenStream::from_iter(iter).to_string()
} else {
None
};
(TokenStream::from_iter(iter).to_string(), attr)
}

#[cfg(test)]
Expand All @@ -1766,7 +1795,9 @@ mod tests {
#[test]
fn test_parse_unexposed_tokens() {
fn check(inp: &str, expected: &str) {
assert_eq!(parse_unexposed_tokens(inp), expected);
let (actual, attr) = parse_unexposed_tokens(inp);
assert_eq!(actual, expected);
assert_eq!(attr, None);
}

check("NS_RETURNS_INNER_POINTER const char *", "const char *");
Expand All @@ -1791,5 +1822,13 @@ mod tests {
"API_DEPRECATED_WITH_REPLACEMENT(\"@\\\"com.adobe.encapsulated-postscript\\\"\", macos(10.0,10.14)) NSPasteboardType __strong",
"NSPasteboardType __strong",
);

let (actual, attr) = parse_unexposed_tokens("NS_SWIFT_NONISOLATED NSTextAttachment *");
assert_eq!(actual, "NSTextAttachment *");
assert_eq!(attr, Some(UnexposedAttr::NonIsolated));

let (actual, attr) = parse_unexposed_tokens("NS_SWIFT_UI_ACTOR SEL");
assert_eq!(actual, "SEL");
assert_eq!(attr, Some(UnexposedAttr::UIActor));
}
}
Loading

0 comments on commit 5fa7a76

Please sign in to comment.