Skip to content

Commit

Permalink
Added some initial constraints to the objective-c bindgen stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
simlay authored and emilio committed Mar 16, 2020
1 parent cfd3347 commit 4562ef9
Show file tree
Hide file tree
Showing 17 changed files with 557 additions and 255 deletions.
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

0 comments on commit 4562ef9

Please sign in to comment.