Open burtonageo opened 8 years ago
I would be happy to be the primary driver behind this feature, and any help/comments on the API of the bindings which would be generated, or any mentorship when implementing this feature would be very appreciated.
Hi, thanks for opening this!
I agree this would be nice to have, so if you want to implement this, please do! I'll try to help answer your questions as much as I can :)
To be fair, I wouldn't worry too much about the code generation, if we get the parsing right it'd be straight-forward to do.
Seems like a good way to start would be adapting our IR to support the equivalent to: CXType_ObjCId
, CXType_ObjCClass
, CXType_ObjCSel
, CXType_ObjCInterface
and `CXType_ObjCObjectPointer
, then also handle the different cursor kinds too.
FWIW, this is the ast generated by your example:
(ObjCClassRef Protocol ObjCInterface
)
(ObjCProtocolDecl Adder Invalid
(ObjCInstanceMethodDecl addNumber:toOtherNumber: Invalid
(ParmDecl a Int
)
(ParmDecl b Int
)
)
)
(ObjCInterfaceDecl Foo ObjCInterface
(ObjCInstanceMethodDecl initWithFirstNumber: Invalid
(TypeRef instancetype Typedef
)
(ParmDecl firstNumber Int
)
)
(ObjCPropertyDecl firstNumber Int
)
(ObjCInstanceMethodDecl firstNumber Invalid
)
)
(ObjCInterfaceDecl Bar ObjCInterface
(FirstRef Foo ObjCInterface
)
(ObjCClassRef Foo ObjCInterface
)
(ObjCProtocolRef Adder Invalid
)
(ObjCPropertyDecl someProperty Int
)
(ObjCInstanceMethodDecl initWithFirstNumber:AndSecondNumber: Invalid
(TypeRef instancetype Typedef
)
(ParmDecl firstNumber Int
)
(ParmDecl secondNumber Int
)
)
(ObjCInstanceMethodDecl frobnicate Invalid
)
(ObjCInstanceMethodDecl getDescriptionFor:atIndex: Invalid
(ParmDecl object ObjCId
)
(ParmDecl index ObjCId
)
)
(ObjCInstanceMethodDecl addNumber:toOtherNumber: Invalid
(ParmDecl a Int
)
(ParmDecl b Int
)
)
(ObjCInstanceMethodDecl someProperty Invalid
)
(ObjCInstanceMethodDecl setSomeProperty: Invalid
(ParmDecl someProperty Int
)
)
)
And there are a bunch of objective C related methods in the cursor docs.
Let me know if you need any help. Thanks for this again! :)
cc @nox who has been working on related things.
Okay, I've had a look at the structure of the code. It looks like (from a 1000 metre view) that the main entry point creates a BindgenContext
, which parses the initial file and turns it into a TranslationUnit
. Item
s are then parsed from the header and added to the BindgenContext
, which then writes out the bindings.
Therefore, extending the parser to support objc should mostly require expanding out the Type
variant of Item
.
One issue I've come across so far is that bindgen
won't parse objective-C declarations without passing in -x objective-c
to the clang args. I've found an old blog post, which seems to suggest that it's a bug in the clang parser, but I'm not sure at the moment.
Yep, that sounds accurate, you probably need to add more TypeKind
s, and potentially more ItemKind
s.
IIRC with clang 3.9 it parses the file automatically as objc if it ends with .m
, but you're right by default it parses .h
files as C. We support passing flags to libclang though, so this should not be a problem.
We started getting some Objective-C support, didn't we?
Right, it's in progress though.
So over the last year I've been adding more and more of this features and it's getting to a good place I think.
Anyway, I'd like to summarize what the features are really getting to:
id
s that represent the objective-c class instances and a trait which has the getters, setters, and methods for that class. Take for example an objective-c class Foo
, you end up with struct Foo(id)
and trait IFoo
(I
for "interface") then impl IFoo for Foo {}
.Foo
inherits Bar
now, the rust bindings will generate impl IFoo for Foo {}
and impl IBar for Foo {}
I spent a bunch of time the other day trying to take advantage of the nullability attributes in clang and hopefully return Option<_>, with the None case matching to a nil but I couldn't figure out how to get the dumb attribute out of clang.
The other thing I spent a bunch of time exploring was how to determine "safety" for functions but I just don't think there's a way to make it "safe" with any assurance. One way to do this, is to add a "safe list" concept that's specified by the user. I just think that if you'd make it too easy for a user to add safe stuff it could get ignored. Thoughts?
With the Objective-C crate, the machinery to send messages is available in stable Rust. From there, it should be possible to parse Objective-C headers, and generate Rust bindings for them.
This would greatly help with integrating Rust into existing projects, especially iOS and Mac apps.
Some ideas about how this could be done:
id
. Class methods and properties can generate an anonymous impl, which calls tomsg_send!
. Note that Objective-C has no concept of 'const
' methods, so every generated method will take&mut self
.A
andB
, whereB
is inherited fromA
, then we canimpl Deref<Target=A> for B { .. }
andimpl DerefMut<Target=A> for B
. Because Objective-C only has single inheritance, there should be no ambiguity about which class to deref to. Base classes won't need this.msg_send!
on the pointer returned by this method.impl
blocks on a type in a crate, so extensions can just open up anotherimpl
block.A probably-incomplete example which ties these ideas together:
Could generate:
Note that these bindings are not the most ergonomic to use: each method is
unsafe
, each variable has to be declaredmut
to actually call any methods on it, and null pointer checking has to be performed manually (it may be worth adding anis_null
method to NSObject, or to a new trait which is automatically implemented by objc base objects). However, they take a lot of the tedium out of writing bindings manually. Furthermore, it could be possible to refine the ergonomics further:Option<_>
, with theNone
case matching to anil
. If we can parse the the new nullability attributes, we could probably just return the object if it was declared_Nonnull
in the header.Mutable
was present to decide whether methods should pass&self
or&mut self
, or perhaps it might be possible to add a custom attribute, e.g.__attribute__((rust_objc_mutating))
to method declarations. Of course, Apple might also decide to add their own mutability attributes to Clang later.msg_send!
in anunsafe
block and just generate safefn
s.PhantomData<T>
in the struct. To maintain optional dynamism, this would require a new trait (e.g.AnyObject
) which all objc objects implemented, and theT
generic would require. With this in place,NSArray
could be declared asstruct NSArray<T: AnyObject = NSObject>(id, PhantomData<T>)
.std
. There is a clear relationNSCopying
andClone
, and everyNSObject
-derived class can implementstd::hash::Hash
courtesy of thehash
method, and eachNSObject
type can bePartialEq
with theisEqual:
method.