Skip to content

Commit

Permalink
Refactor property parsing
Browse files Browse the repository at this point in the history
Fixes their order to be the same as in the source file
  • Loading branch information
madsmtm committed Oct 7, 2022
1 parent 8f1a9ed commit dedf7d4
Show file tree
Hide file tree
Showing 124 changed files with 2,499 additions and 2,241 deletions.
2 changes: 2 additions & 0 deletions header-translator/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ pub struct Config {
pub struct ClassData {
#[serde(default)]
pub methods: HashMap<String, MethodData>,
#[serde(default)]
pub properties: HashMap<String, MethodData>,
}

#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
Expand Down
1 change: 1 addition & 0 deletions header-translator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod availability;
mod config;
mod method;
mod objc2_utils;
mod property;
mod rust_type;
mod stmt;

Expand Down
88 changes: 52 additions & 36 deletions header-translator/src/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ use proc_macro2::TokenStream;
use quote::{format_ident, quote, ToTokens, TokenStreamExt};

use crate::availability::Availability;
use crate::config::ClassData;
use crate::config::MethodData;
use crate::objc2_utils::in_selector_family;
use crate::rust_type::{RustType, RustTypeReturn};

#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
enum Qualifier {
pub enum Qualifier {
In,
Inout,
Out,
Expand All @@ -18,7 +18,7 @@ enum Qualifier {
}

impl Qualifier {
fn parse(qualifiers: ObjCQualifiers) -> Self {
pub fn parse(qualifiers: ObjCQualifiers) -> Self {
match qualifiers {
ObjCQualifiers {
in_: true,
Expand Down Expand Up @@ -74,7 +74,7 @@ impl Qualifier {
}

#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
enum MemoryManagement {
pub enum MemoryManagement {
/// Consumes self and returns retained pointer
Init,
ReturnsRetained,
Expand Down Expand Up @@ -112,36 +112,58 @@ impl MemoryManagement {
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct Method {
selector: String,
fn_name: String,
availability: Availability,
is_class_method: bool,
is_optional_protocol_method: bool,
memory_management: MemoryManagement,
designated_initializer: bool,
arguments: Vec<(String, Option<Qualifier>, RustType)>,
result_type: RustTypeReturn,
safe: bool,
pub selector: String,
pub fn_name: String,
pub availability: Availability,
pub is_class: bool,
pub is_optional_protocol: bool,
pub memory_management: MemoryManagement,
pub designated_initializer: bool,
pub arguments: Vec<(String, Option<Qualifier>, RustType)>,
pub result_type: RustTypeReturn,
pub safe: bool,
}

impl Method {
/// Takes one of `EntityKind::ObjCInstanceMethodDecl` or
/// `EntityKind::ObjCClassMethodDecl`.
pub fn parse(entity: Entity<'_>, class_data: Option<&ClassData>) -> Option<Self> {
// println!("Method {:?}", entity.get_display_name());
pub fn partial(entity: Entity<'_>) -> PartialMethod<'_> {
let selector = entity.get_name().expect("method selector");
let fn_name = selector.trim_end_matches(|c| c == ':').replace(':', "_");

let data = class_data
.map(|class_data| {
class_data
.methods
.get(&fn_name)
.copied()
.unwrap_or_default()
})
.unwrap_or_default();
let is_class = match entity.get_kind() {
EntityKind::ObjCInstanceMethodDecl => false,
EntityKind::ObjCClassMethodDecl => true,
_ => unreachable!("unknown method kind"),
};

PartialMethod {
entity,
selector,
is_class,
fn_name,
}
}
}

#[derive(Debug, Clone)]
pub struct PartialMethod<'tu> {
entity: Entity<'tu>,
selector: String,
pub is_class: bool,
pub fn_name: String,
}

impl<'tu> PartialMethod<'tu> {
pub fn parse(self, data: MethodData) -> Option<Method> {
let Self {
entity,
selector,
is_class,
fn_name,
} = self;

// println!("Method {:?}", selector);
if data.skipped {
return None;
}
Expand All @@ -157,12 +179,6 @@ impl Method {
.expect("method availability"),
);

let is_class_method = match entity.get_kind() {
EntityKind::ObjCInstanceMethodDecl => false,
EntityKind::ObjCClassMethodDecl => true,
_ => unreachable!("unknown method kind"),
};

let arguments: Vec<_> = entity
.get_arguments()
.expect("method arguments")
Expand Down Expand Up @@ -274,12 +290,12 @@ impl Method {
memory_management.verify_sel(&selector);
}

Some(Self {
Some(Method {
selector,
fn_name,
availability,
is_class_method,
is_optional_protocol_method: entity.is_objc_optional(),
is_class,
is_optional_protocol: entity.is_objc_optional(),
memory_management,
designated_initializer,
arguments,
Expand Down Expand Up @@ -338,7 +354,7 @@ impl ToTokens for Method {

let unsafe_ = if self.safe { quote!() } else { quote!(unsafe) };

let result = if self.is_class_method {
let result = if self.is_class {
quote! {
pub #unsafe_ fn #fn_name(#(#fn_args),*) #ret {
#macro_name![Self::class(), #method_call]
Expand All @@ -355,7 +371,7 @@ impl ToTokens for Method {
}
}

fn handle_reserved(s: &str) -> &str {
pub(crate) fn handle_reserved(s: &str) -> &str {
match s {
"type" => "type_",
"trait" => "trait_",
Expand Down
173 changes: 173 additions & 0 deletions header-translator/src/property.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
use clang::{Entity, EntityKind, EntityVisitResult, Nullability, ObjCAttributes};
use proc_macro2::TokenStream;
use quote::ToTokens;

use crate::availability::Availability;
use crate::config::MethodData;
use crate::method::{MemoryManagement, Method, Qualifier};
use crate::rust_type::{RustType, RustTypeReturn};

#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct Property {
name: String,
getter_name: String,
setter_name: Option<String>,
availability: Availability,
is_class: bool,
is_optional_protocol: bool,
type_in: RustType,
type_out: RustType,
safe: bool,
}

impl Property {
/// Takes `EntityKind::ObjCPropertyDecl`.
pub fn partial(entity: Entity<'_>) -> PartialProperty<'_> {
let attributes = entity.get_objc_attributes();
let has_setter = attributes.map(|a| !a.readonly).unwrap_or(true);

PartialProperty {
entity,
name: entity.get_display_name().expect("property getter name"),
getter_name: entity.get_objc_getter_name().expect("property getter name"),
setter_name: has_setter.then(|| {
entity
.get_objc_setter_name()
.expect("property setter name")
.trim_end_matches(|c| c == ':')
.to_string()
}),
is_class: attributes.map(|a| a.class).unwrap_or(false),
attributes: entity.get_objc_attributes(),
}
}
}

#[derive(Debug, Clone)]
pub struct PartialProperty<'tu> {
entity: Entity<'tu>,
pub name: String,
pub getter_name: String,
pub setter_name: Option<String>,
pub is_class: bool,
attributes: Option<ObjCAttributes>,
}

impl PartialProperty<'_> {
pub fn parse(self, data: MethodData) -> Option<Property> {
let Self {
entity,
name,
getter_name,
setter_name,
is_class,
attributes,
} = self;

if data.skipped {
return None;
}

// println!("Property {getter_name:?}/{setter_name:?}: {attributes:?}");

let availability = Availability::parse(
entity
.get_platform_availability()
.expect("method availability"),
);

// `@property(copy)` for some reason returns nonnull?
//
// Swift signifies that they use forced unwrapping here, perhaps
// because they know that it can fail (e.g. in OOM situations), but
// is very unlikely to?
let default_nullability = if attributes.map(|a| a.copy).unwrap_or(false) {
Nullability::NonNull
} else {
Nullability::Unspecified
};

let type_in = RustType::parse_property(
entity.get_type().expect("property type"),
Nullability::Unspecified,
);
let type_out = RustType::parse_property(
entity.get_type().expect("property type"),
default_nullability,
);

let mut memory_management = MemoryManagement::Normal;

entity.visit_children(|entity, _parent| {
match entity.get_kind() {
EntityKind::ObjCClassRef
| EntityKind::ObjCProtocolRef
| EntityKind::TypeRef
| EntityKind::ParmDecl => {
// Ignore
}
EntityKind::ObjCReturnsInnerPointer => {
if memory_management != MemoryManagement::Normal {
panic!("got unexpected ObjCReturnsInnerPointer")
}
memory_management = MemoryManagement::ReturnsInnerPointer;
}
EntityKind::ObjCInstanceMethodDecl => {
println!("method in property: {entity:?}");
}
EntityKind::UnexposedAttr => {}
_ => panic!("Unknown property child: {:?}, {:?}", entity, _parent),
};
EntityVisitResult::Continue
});

let qualifier = entity.get_objc_qualifiers().map(Qualifier::parse);
assert_eq!(qualifier, None, "properties do not support qualifiers");

Some(Property {
name,
getter_name,
setter_name,
availability,
is_class,
is_optional_protocol: entity.is_objc_optional(),
type_in,
type_out,
safe: !data.unsafe_,
})
}
}

impl ToTokens for Property {
fn to_tokens(&self, tokens: &mut TokenStream) {
let method = Method {
selector: self.getter_name.clone(),
fn_name: self.getter_name.clone(),
availability: self.availability.clone(),
is_class: self.is_class,
is_optional_protocol: self.is_optional_protocol,
memory_management: MemoryManagement::Normal,
designated_initializer: false,
arguments: Vec::new(),
result_type: RustTypeReturn::new(self.type_out.clone()),
safe: self.safe,
};
method.to_tokens(tokens);
if let Some(setter_name) = &self.setter_name {
let method = Method {
selector: setter_name.clone() + ":",
fn_name: setter_name.clone(),
availability: self.availability.clone(),
is_class: self.is_class,
is_optional_protocol: self.is_optional_protocol,
memory_management: MemoryManagement::Normal,
designated_initializer: false,
arguments: Vec::from([(self.name.clone(), None, self.type_in.clone())]),
result_type: RustTypeReturn::new(RustType::Void),
safe: self.safe,
};
method.to_tokens(tokens);
}
}
}
Loading

0 comments on commit dedf7d4

Please sign in to comment.