Skip to content

Higher level objective-c support. #1722

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 78 additions & 49 deletions src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3394,9 +3394,12 @@ impl TryToRustTy for Type {
TypeKind::ObjCSel => Ok(quote! {
objc::runtime::Sel
}),
TypeKind::ObjCId | TypeKind::ObjCInterface(..) => Ok(quote! {
TypeKind::ObjCId => Ok(quote! {
id
}),
TypeKind::ObjCInterface(..) => Ok(quote! {
objc::runtime::Object
}),
ref u @ TypeKind::UnresolvedTypeRef(..) => {
unreachable!("Should have been resolved after parsing {:?}!", u)
}
Expand Down Expand Up @@ -3644,7 +3647,7 @@ fn objc_method_codegen(
method: &ObjCMethod,
class_name: Option<&str>,
prefix: &str,
) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
) -> proc_macro2::TokenStream {
let signature = method.signature();
let fn_args = utils::fnsig_arguments(ctx, signature);
let fn_ret = utils::fnsig_return_ty(ctx, signature);
Expand All @@ -3665,15 +3668,13 @@ fn objc_method_codegen(
let methods_and_args = method.format_method_call(&fn_args);

let body = if method.is_class_method() {
let class_name = class_name
.expect("Generating a class method without class name?")
.to_owned();
let expect_msg = proc_macro2::Literal::string(&format!(
"Couldn't find {}",
let class_name = ctx.rust_ident(
class_name
));
.expect("Generating a class method without class name?")
.to_owned(),
);
quote! {
msg_send!(objc::runtime::Class::get(#class_name).expect(#expect_msg), #methods_and_args)
msg_send!(class!(#class_name), #methods_and_args)
}
} else {
quote! {
Expand All @@ -3684,16 +3685,11 @@ fn objc_method_codegen(
let method_name =
ctx.rust_ident(format!("{}{}", prefix, method.rust_name()));

(
quote! {
unsafe fn #method_name #sig {
#body
}
},
quote! {
unsafe fn #method_name #sig ;
},
)
quote! {
unsafe fn #method_name #sig where <Self as std::ops::Deref>::Target: objc::Message + Sized {
#body
}
}
}

impl CodeGenerator for ObjCInterface {
Expand All @@ -3708,13 +3704,10 @@ impl CodeGenerator for ObjCInterface {
debug_assert!(item.is_enabled_for_codegen(ctx));

let mut impl_items = vec![];
let mut trait_items = vec![];

for method in self.methods() {
let (impl_item, trait_item) =
objc_method_codegen(ctx, method, None, "");
let impl_item = objc_method_codegen(ctx, method, None, "");
impl_items.push(impl_item);
trait_items.push(trait_item)
}

let instance_method_names: Vec<_> =
Expand All @@ -3724,61 +3717,97 @@ impl CodeGenerator for ObjCInterface {
let ambiquity =
instance_method_names.contains(&class_method.rust_name());
let prefix = if ambiquity { "class_" } else { "" };
let (impl_item, trait_item) = objc_method_codegen(
let impl_item = objc_method_codegen(
ctx,
class_method,
Some(self.name()),
prefix,
);
impl_items.push(impl_item);
trait_items.push(trait_item)
}

let trait_name = ctx.rust_ident(self.rust_name());

let trait_constraints = quote! {
Sized + std::ops::Deref
};
let trait_block = if self.is_template() {
let template_names: Vec<Ident> = self
.template_names
.iter()
.map(|g| ctx.rust_ident(g))
.collect();

quote! {
pub trait #trait_name <#(#template_names),*>{
#( #trait_items )*
pub trait #trait_name <#(#template_names),*> : #trait_constraints {
#( #impl_items )*
}
}
} else {
quote! {
pub trait #trait_name {
#( #trait_items )*
pub trait #trait_name : #trait_constraints {
#( #impl_items )*
}
}
};

let ty_for_impl = quote! {
id
};
let impl_block = if self.is_template() {
let template_names: Vec<Ident> = self
.template_names
.iter()
.map(|g| ctx.rust_ident(g))
.collect();
quote! {
impl <#(#template_names :'static),*> #trait_name <#(#template_names),*> for #ty_for_impl {
#( #impl_items )*
let class_name = ctx.rust_ident(self.name());
if !self.is_category() && !self.is_protocol() {
let struct_block = quote! {
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct #class_name(pub id);
impl std::ops::Deref for #class_name {
type Target = objc::runtime::Object;
fn deref(&self) -> &Self::Target {
unsafe {
&*self.0
}
}
}
}
} else {
quote! {
impl #trait_name for #ty_for_impl {
#( #impl_items )*
unsafe impl objc::Message for #class_name { }
impl #class_name {
pub fn alloc() -> Self {
Self(unsafe {
msg_send!(objc::class!(#class_name), alloc)
})
}
}
};
result.push(struct_block);
for protocol_id in self.conforms_to.iter() {
let protocol_name = ctx.rust_ident(
ctx.resolve_type(protocol_id.expect_type_id(ctx))
.name()
.unwrap(),
);
let impl_trait = quote! {
impl #protocol_name for #class_name { }
};
result.push(impl_trait);
}
};
}

if !self.is_protocol() {
let impl_block = if self.is_template() {
let template_names: Vec<Ident> = self
.template_names
.iter()
.map(|g| ctx.rust_ident(g))
.collect();
quote! {
impl <#(#template_names :'static),*> #trait_name <#(#template_names),*> for #class_name {
}
}
} else {
quote! {
impl #trait_name for #class_name {
}
}
};
result.push(impl_block);
}

result.push(trait_block);
result.push(impl_block);
result.saw_objc();
}
}
Expand Down
21 changes: 16 additions & 5 deletions src/ir/objc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ pub struct ObjCInterface {
/// The list of template names almost always, ObjectType or KeyType
pub template_names: Vec<String>,

conforms_to: Vec<ItemId>,
/// The list of protocols that this interface conforms to.
pub conforms_to: Vec<ItemId>,

/// List of the methods defined in this interfae
methods: Vec<ObjCMethod>,
Expand Down Expand Up @@ -77,15 +78,15 @@ impl ObjCInterface {

/// Formats the name for rust
/// Can be like NSObject, but with categories might be like NSObject_NSCoderMethods
/// and protocols are like protocol_NSObject
/// and protocols are like PNSObject
pub fn rust_name(&self) -> String {
if let Some(ref cat) = self.category {
format!("{}_{}", self.name(), cat)
} else {
if self.is_protocol {
format!("protocol_{}", self.name())
format!("P{}", self.name())
} else {
self.name().to_owned()
format!("I{}", self.name().to_owned())
}
}
}
Expand All @@ -100,6 +101,16 @@ impl ObjCInterface {
&self.methods
}

/// Is this a protocol?
pub fn is_protocol(&self) -> bool {
self.is_protocol
}

/// Is this a category?
pub fn is_category(&self) -> bool {
self.category.is_some()
}

/// List of the class methods defined in this interface
pub fn class_methods(&self) -> &Vec<ObjCMethod> {
&self.class_methods
Expand Down Expand Up @@ -129,7 +140,7 @@ impl ObjCInterface {
}
CXCursor_ObjCProtocolRef => {
// Gather protocols this interface conforms to
let needle = format!("protocol_{}", c.spelling());
let needle = format!("P{}", c.spelling());
let items_map = ctx.items();
debug!("Interface {} conforms to {}, find the item", interface.name, needle);

Expand Down
53 changes: 44 additions & 9 deletions tests/expectations/tests/libclang-3.8/objc_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,56 @@
extern crate objc;
#[allow(non_camel_case_types)]
pub type id = *mut objc::runtime::Object;
pub trait Foo<ObjectType> {
unsafe fn get(self) -> id;
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct Foo(pub id);
impl std::ops::Deref for Foo {
type Target = objc::runtime::Object;
fn deref(&self) -> &Self::Target {
unsafe { &*self.0 }
}
}
unsafe impl objc::Message for Foo {}
impl Foo {
pub fn alloc() -> Self {
Self(unsafe { msg_send!(objc::class!(Foo), alloc) })
}
}
impl<ObjectType: 'static> Foo<ObjectType> for id {
unsafe fn get(self) -> id {
impl<ObjectType: 'static> IFoo<ObjectType> for Foo {}
pub trait IFoo<ObjectType>: Sized + std::ops::Deref {
unsafe fn get(self) -> id
where
<Self as std::ops::Deref>::Target: objc::Message + Sized,
{
msg_send!(self, get)
}
}
pub trait FooMultiGeneric<KeyType, ObjectType> {
unsafe fn objectForKey_(self, key: id) -> id;
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct FooMultiGeneric(pub id);
impl std::ops::Deref for FooMultiGeneric {
type Target = objc::runtime::Object;
fn deref(&self) -> &Self::Target {
unsafe { &*self.0 }
}
}
unsafe impl objc::Message for FooMultiGeneric {}
impl FooMultiGeneric {
pub fn alloc() -> Self {
Self(unsafe { msg_send!(objc::class!(FooMultiGeneric), alloc) })
}
}
impl<KeyType: 'static, ObjectType: 'static>
IFooMultiGeneric<KeyType, ObjectType> for FooMultiGeneric
{
}
impl<KeyType: 'static, ObjectType: 'static> FooMultiGeneric<KeyType, ObjectType>
for id
pub trait IFooMultiGeneric<KeyType, ObjectType>:
Sized + std::ops::Deref
{
unsafe fn objectForKey_(self, key: id) -> id {
unsafe fn objectForKey_(self, key: id) -> id
where
<Self as std::ops::Deref>::Target: objc::Message + Sized,
{
msg_send!(self, objectForKey: key)
}
}
53 changes: 44 additions & 9 deletions tests/expectations/tests/libclang-3.9/objc_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,56 @@
extern crate objc;
#[allow(non_camel_case_types)]
pub type id = *mut objc::runtime::Object;
pub trait Foo<ObjectType> {
unsafe fn get(self) -> id;
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct Foo(pub id);
impl std::ops::Deref for Foo {
type Target = objc::runtime::Object;
fn deref(&self) -> &Self::Target {
unsafe { &*self.0 }
}
}
unsafe impl objc::Message for Foo {}
impl Foo {
pub fn alloc() -> Self {
Self(unsafe { msg_send!(objc::class!(Foo), alloc) })
}
}
impl<ObjectType: 'static> Foo<ObjectType> for id {
unsafe fn get(self) -> id {
impl<ObjectType: 'static> IFoo<ObjectType> for Foo {}
pub trait IFoo<ObjectType>: Sized + std::ops::Deref {
unsafe fn get(self) -> id
where
<Self as std::ops::Deref>::Target: objc::Message + Sized,
{
msg_send!(self, get)
}
}
pub trait FooMultiGeneric<KeyType, ObjectType> {
unsafe fn objectForKey_(self, key: id) -> id;
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct FooMultiGeneric(pub id);
impl std::ops::Deref for FooMultiGeneric {
type Target = objc::runtime::Object;
fn deref(&self) -> &Self::Target {
unsafe { &*self.0 }
}
}
unsafe impl objc::Message for FooMultiGeneric {}
impl FooMultiGeneric {
pub fn alloc() -> Self {
Self(unsafe { msg_send!(objc::class!(FooMultiGeneric), alloc) })
}
}
impl<KeyType: 'static, ObjectType: 'static>
IFooMultiGeneric<KeyType, ObjectType> for FooMultiGeneric
{
}
impl<KeyType: 'static, ObjectType: 'static> FooMultiGeneric<KeyType, ObjectType>
for id
pub trait IFooMultiGeneric<KeyType, ObjectType>:
Sized + std::ops::Deref
{
unsafe fn objectForKey_(self, key: id) -> id {
unsafe fn objectForKey_(self, key: id) -> id
where
<Self as std::ops::Deref>::Target: objc::Message + Sized,
{
msg_send!(self, objectForKey: key)
}
}
Loading